내 서버의 시간을 설정하는 것이다.

NTP(Network Time Protocol)을 이용하여 동기화를 할 것이다.
첫번째 작업은 내 지역의 timezone으로 설정하는 것이고.
그 후 NTP 서비스를 돌리는 것이다.

NTP서버에서 주기적으로 올바른 시간을 맞추어 시간을 정확하게 동기화 시킬것이다.

먼저 우리가 설정할 수 있는 timezone을 확인해본다.



(timezone 확인)



우리는 Asia/Seoul을 선택할 것이다.



(아시아/서울)



아시아/서울 로 timezone을 설정한다.



(timezone 설정)



timedatectl 명령어로 확인해본다.



(설정 확인)



이제 NTP를 이용할 것인데,

기본 CentOS에 설치가 안되어있으므로 설치한다!



(NTP 설치)



이제 ntpd 를 실행시키면 끝!



(실행)



이것으로 서버 시간 설정이 끝난다. :)


내가 선택한 운영체제는 CentOS7 버전이다.

7 이전의 CentOS에서는 iptables-service 를 이용해 방화벽을 설정했었는데
7이후에는 firewalld 를 통해 방화벽 설정을 한다.

iptables는 존재한다. 다만, iptables를 수정하는데 7이후에서는 firewalld를 이용하는 것 뿐이다.

이 프로젝트를 진행하면서 필요한 설정을 하겠지만
처음 기본으로 방화벽을 설정할 것이다.

firewall-cmd --state명령으로 현재 상태를 확인한다.



(현재 상태 확인)


확인해보니 not found??...

firewalld를 설치해준다.! (없나보다!)



(firewalld 설치)



설치 후 firewalld를 시작해주고, enable을 통해 boot될 때 실행되도록 설정해준다.
(서버라서 종료할 일은 없지만.!)

그리고 마지막 명령은 상태를 확인할 수 있는 명령이다.



(firewalld 시작)


위에서 확인했지만,

처음 사용한 명령으로 다시 확인해보겠다.




(실행중)


현재 상태를 보니 실행되는 중이다.

가장 중요한 우리는 앞으로 SSH를 이용해 접근할 것이므로 SSH서비스를 등록해둔다.
포트 이름을 지정해도 되지만 서비스 이름으로도 설정 할 수 있다.

ssh 포트를 수정하지 않았기 때문에 사용 가능!



(SSH 등록)


그 후 우리는 웹 호스팅을 할 것이기 때문에 http도 추가!


(http 추가)




SSL/TLS를 사용할 것이므로 https도 추가!


(https 추가)





이제 추가한 것들을 확인해보자


(확인)


지금까지 입력한 서비스들이 services에 등록되어 있는 것을 볼 수 있다.

지금까지 설정한 것들은 default 존인데, conf 파일에 보면 default 존이 public 존으로 설정되어있는 것을 볼 수 있다.

이제 설정한 것들을 올린다.



(설정 등록)



그 후 enable을 다시 주어서 boot 될 때 실행이 되도록 설정한다.



(enable)


VPS 웹 호스팅 프로젝트를 시작해본다.

웹 호스팅 서비스를 받지 않고, 직접 서버를 대여하여 가꾸는 것이다!
사실 웹 호스팅뿐 아니라 다양한 테스팅을 위해 서버를 대여하고 싶었다.

찾아본 곳 중 DigitalOcean 이라는 사이트가 있었다.
가장 저렴한 옵션으로 한달에 5딸라!

그리고 내가 적은 링크로 가입을 하게되면 10딸라를 준다.
고로 아래의 링크로 가입을 할 경우, 무료 2달을 체험할 수 있다.!

(본인만의 서버를 만져보고 싶고, 가꿔보고 싶은데 아직 잘 몰라서 서버 대여에 돈을 지불하기가 고민되는 분들은 일단 2달만 써볼 수 있는 좋은 기회이다!)


https://m.do.co/c/88300c36e548


요 링크로 들어가서 가입을 하고 Droplets를 생성할 수 있다.

Droplets 생성 방법은 직관적이어서 생략한다.
사용하고 싶은 운영체제를 선택하고 옵션을 선택하여 만드는 방식이다.

다 만들게 되면 아래와 같이 서버가 할당된다.



(서버 생성)


나는 리눅스 수업을 CentOS로 배웠어서 CentOS를 택했다.
우분투가 많이 사용하지만 우분투는 운영체제때만 잠깐 써봤던지라 CentOS가 더 친숙했다.

가입할 때 적은 메일로 편지 2통이 날라와 있다.

하나는 내가 CentOS를 선택했기 때문에 온건데
처음 설정하는 방법에 대해 알려준다. (매우매우 친절하다.!)

DigitalOcean의 장점 중 하나는 이것인거 같다.

서버에 대한 지식이 부족할 지라도 DigitalOcean에서의 튜토리얼도 있고 처음 시작할 때 뭐를 설정해야하는지 등등 매우 친절하고, 이런것을 직접 하므로써 공부도 많이 된다.




(설정 방법)



스텝별로 나와있다.

또 하나의 메일은 방금 생성한 서버에 대한 정보이다.



(서버 정보)



이 메일 안에는
IP주소, 처음 root의 비밀번호 등 서버에 관한 정보가 담겨있다.

나는 Xshell 을 이용하므로 Xshell로 나의 서버에 접속하였다.



(첫 접속)


두근두근
나의 첫 서버라니!(가상 사설서버이지만.!)

처음 들어가게 되면 비밀번호 치라고 나오는데, 아까 메일에서 적혀있는 비밀번호를 입력하면 된다.
그렇게 되면 이제 새로운 비밀번호를 치라고 나오는데 이것이 바로 root 계정의 비밀번호이다.

root 비밀번호도 새로 정했다면,
이제 내가 접속할 계정을 추가해야한다.
root 계정은 막강하고 중요하니까! 일반 계정을 하나 만들어주고, sudo 명령을 사용할 수 있도록 권한까지 부여할 것이다. 어찌보면 나의 부root계정인 셈이다.

아이디 생성


(계정 생성)



그 후 sudo 권한 즉 super user 권한을 줄것이다.
/etc/sudoers 라는 파일을 수정해야한다.

기본적으로 쓰기 권한이 없다. 그러므로 쓰기 권한을 추가하고 이 파일에 들어가서 수정해주어야한다.



(쓰기 권한 추가)


그 후 맨 아래에 다음 설정을 추가해준다.



(설정 추가)


위 설정은 normaltic이라는 나의 계정에 대해 supersuer권한을 주고
sudo 명령을 사용할 때 비밀번호를 묻지 않게 설정한 것이다.

설정이 끝나면 쓰기 권한을 제거해준다.



(쓰기 권한 제거)



그렇다면 normaltic 계정으로 들어가서 sudo 명령이 되는지 확인!



(확인)



잘된다!

자 이제 SSH키를 이용해 접속할 것이다.
이 부분은 DigitalOcean에서도 권장하는 방법으로 비밀번호로 로그인하는 것보다 더 안전하기 때문이다.
SSH 공개키를 이용하는 방법이다. 로컬 컴퓨터인 내가 쓰는 컴퓨터에서 키를 가지고 있고 그에 해당하는 공개키를 나의 서버에 저장해두는 것이다.
그렇게 하여 계정 비밀번호로 접속하는 것이 아니라 SSH 공개키 인증을 통해 접속하는 방법이다.
(SSH 원리가 궁금하다면 구글링!)

SSH 키 생성하는 방법은 여러가지가 있지만
Xshell에서 하는 방법을 이용했다.

도구 - 사용자 키 관리자

로 들어간다.



(사용자 키 관리자)



들어가게 되면 아래와 같은 창이 뜬다.



(사용자 키)


여기서 생성 버튼을 누른다.


(키 선택)



키의 유형과 키 길이를 선택할 수 있다.

그렇게 선택을 마치면 키를 생성한다.



(키 생성)



키 생성이 된 후

이 키를 사용할 때마다 사용할 암호를 정한다.



(사용자 키 암호)



??
비밀번호를 안쓴다고 했는데 왠 비밀번호?

SSH 키를 우리가 생성했고, 이 키를 이용해 접속한다고 했다. 즉, 계정의 비밀번호로 접속하는 것이 아니라 SSH 인증을 통해 접속한다.
그렇기에 이 키파일은 굉장히 중요하다. 만약 다른사람이 내 컴퓨터에 덜컥 앉아서 접속을 할 수도 있는것 아니겠는가? 그렇기에 이 키 파일에 접근하기 위한 비밀번호이다.

이제 등록된 공개키를 서버에 저장해야 한다.

SSH의 키 파일이 어디로 설정되어있는지 확인해보자.



(설정파일)



이 파일에서 확인해보면



(키파일 위치)



인증키파일 위치는 위와 같이 되어있다.
본인이 수정하고 싶으면 수정 할 수 있다. 해당 계정의 홈 디렉토리에서 위의 경로이다.

그렇다면 적어준다!


(인증키 설정)


위의 경로에서 아까 생성한 키파일 등록정보를 가보면 공개키를 볼 수 있다.
그 공개키를 위 파일에 저장한다.

그 결과 안되었다... CentOS는 조금 다른가보다.!
검색을 이리저리 뒤적거리다 보니 다른분이 이미 이것으로 많은 시간을 썼던 경험을 공유해주셨다.
그 분 덕분에 나는 조금 덜 헤멜 수 있었다.

아까 그 설정파일에서 주석처리 된 부분을 제거해주어야했다.



(주석 제거)



공개키인증을 사용하는 것에 있어 기본 설정으로 #이 붙어있으니 주석을 제거해준다.

그 후 권한 설정인데...
사실 이유는 잘 모르겠다. ~/.ssh/authorized_keys 권한 설정이 해당 권한보다 높으면 안된다니..

정확하게 .ssh 디렉토리 권한은 0700
정확하게 authorized_keys 파일의 권한은 0600
이보다 높아서도 안된다. 권한이 더 많으면 될 줄 알았는데 많으면 오히려 안된다고 한다..

권한 설정!



(디렉토리 권한)



(키파일 권한)



그 후 SSH로 접속해본다.


(접속 성공)



SSH 설정까지 꽤 오랜 시간이 걸렸다.

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
위 코드는 windows 프로그래밍에서 많이 사용된다.

windows 프로그래밍하는 방법은 크게 두가지가 있는데
MFC(Microsoft Foundation Class)를 사용하는 방법과
SDK(Software Development Kit)를 사용하는 방법이다.

MFC는 SDK와 비교하면 복잡하고 많은 기능들이 들어가있다.

만약 MFC를 사용하지 않는다면 MFC에 있는 많은 자료구조와 새로운 데이터 정의, 데이터타입은 필요하지 않다. 그렇기에 만약 MFC를 사용하지 않는다면 이 모든것을 헤더에 포함시킨다면 큰 오버헤드가 되는 것이다.

그렇기에
#define WIN32_LEAN_AND_MEAN
는 컴파일러에게 외부 MFC 오버헤드를 포함하지 말라고 알려준다.

위 문장을 #include <windows.h> 이전에 선언해 주면 많은 헤더를 include하면서 중복되는 헤더를 방지하고 데이터의 중복정의를 제거해준다. 전체적으로 프로그램의 사이즈를 줄여주고, 빌드 속도를 높여주는 효과가 있다.


Windows Programing 중 가장 기초인 창만들어서 띄우는 것을 할 것이다.
창 이름은 Hellow World로 만들것이다.

먼저, 함수 선언과 전역변수, 상수 선언이다.



(선언)



윈도우 프로그래밍에서는 보통 windows.h 헤더를 include한다.
우리는 MFC를 사용하지 않을 것이므로, WIN32_LEAN_AND_MEAN을 define해준다.

윈도우 프로그래밍에서의 시작점은 WinMain 함수이다.



(WinMain)



기존 C코딩에서 main 함수와 같다.
보면 우리는 CreateMainWindow 함수를 만들어 두어 이 함수로 창을 만들것이고
그 아래에는 메인 메시지 루프가 있다. 메시지 루프는 윈도우 프로그래밍에서 중요한 부분이다.
윈도우 프로그램은 윈도우와의 메시지를 주고 받으면서 동작을 한다. 그렇기에 오고 가는 윈도우 메시지를 확인하는 부분이라고 생각하면 된다.

그렇다면 CreateMainWindow 함수를 보자.
윈도우를 표시하기 전에 Window 클래스를 만들고 등록해야하는데 그 작업을 하는 함수이다.

윈도우의 초기 설정을 하는 클래스가 바로 WNDCLASSEX 구조체이고 이 안의 멤버값들을 채워넣어 윈도우를 설정한다.



(WNDCLASSEX 설정)



설정이 끝나면 클래스를 등록하는 부분이 이어진다.




(클래스 등록)



설정한 wcx 구조체를 넣어주어 클래스를 등록한다.

그 후 창을 생성하는 CreateWindow 함수가 나온다. (각 인자에 대한 설명은 따로 포스팅할 것이다.)



(창 생성)



창을 생성하고 ShowWindow 함수를 이용하여 윈도우를 표시하고
UpdateWindow를 통해 윈도우 프로시저에게 자기 자신을 그리라고 메시지를 보낸다.

* WinProc 함수
중요한 함수이다.
위에서 윈도우 프로그램은 윈도우와 메시지를 주고 받으면서 동작한다고 했다. 그 메시지를 처리하는데 사용되는 함수이다.
위에서 설정한 WNDCLASSEX 구조체 안에 명시된 이름과 동일해야한다. 여기서는 WinProc

무시하는 메시지들은 윈도우가 알아서 처리한다. 여기서는 윈도우가 종료될 때의 메시지만 작성할 것이다.



(WinProc)



메시지가 WM_DESTORY라면 (윈도우가 종료되는 메시지)
PostQuitMessage(0)을 통해 프로그램이 종료를 요청한다고 윈도우에게 신호를 보내는 것이다.
그러면 메시지큐에 WM_QUIT이 추가되고 WinMain에서 이 메시지를 보게 되면 루프를 빠져나가 프로그램이 종료되게 되는 것이다.

컴파일 해서 프로그램을 실행해본다.



(윈도우 창 생성)



우리가 원하는 Hello World 창이 생성되었다.

(*참조 - 2D 게임 프로그래밍, 찰스 켈리)

이번에 분석한 자료는 house_of_spirit이다.

house_of_spirit 공격의 목적은 malloc의 return 결과를 우리가 원하는 청크의 주소로 만드는 것이다.
즉, freelist에 우리가 원하는 위치의 주소가 들어갈 수 있게 하는 것이다.

이 공격을 사용할 수 있는 조건은
우리가 메모리 영역에 가짜 청크를 만들 수 있어야한다는 것이다.
가짜 청크를 2개 만들어주는데 연속적으로 있어야한다.
또, 그 가짜 청크 주소를 free 함수에 넘겨 줄 수 있어야한다.
* free하기 전 malloc 1번은 꼭 필요하다.

먼저 malloc을 해준다.



(malloc)


 이 이유에 대해서 이해가 잘 가지 않았었다.
하지만 주석처리하고 실행해보고 이해할 수 있었다. 뒤에 우리가 가짜 청크를 만들고 그 주소를 free 시킬 것인데, malloc 할당을 한번도 하지 않고 free를 했기 때문에 에러가 난다.
이것을 막기 위해 malloc을 한번 해준다. 1로 해준 것은 그 값이 어떤 값이어도 상관없고 다만, 이 자료에서는 fastbin의 freelist를 이용할 것이기 때문에 작은 값으로 설정한 것이다.

이제 free의 인자로 넘길 포인터 변수를 만들어준다.



(포인터 변수 생성)


a 변수에 우리가 만든 가짜 청크의 주소를 쓸것이다.

이제 가짜 청크의 영역이다. 이 자료에서는 stack영역에 가짜 청크를 만들어주었다.

stack영역에는 비교적 우리가 값을 적기 쉽기 때문에 stack영역에 가짜 청크를 만들어 준 것 같다.



(가짜 청크를 위한 메모리 공간 확보)


위에 __attribute__는 컴파일러에게 특정한 방향으로 컴파일을 해달라고 부탁하는 명령이다.
aligned 16에 의해 16바이트로 정렬되게 컴파일 시킨다. 
64비트에서 unsigned long long은 8바이트이고 16바이트보다 작기 때문에 저 명령을 안써도 상관 없었다. (자료에서는 적혀있었다. 앞으로 64비트 이상의 컴퓨터가 나오게 되면 저 명령어가 필요할 수도 있을 것이다.)

그 다음으로 가짜 청크를 만들어주는 작업이다.



(가짜 청크 생성)


위 설명을 요약하자면
이 자료에서는 fastbin을 쓸것이기 대문에 PREV_INUSE는 무시해도 되지만, 다른 flag비트는 문제가 된다는 말로 모든 flag를 0으로 세팅하였다.
그리고 malloc 구현에 있어서 내부 internal size로 반올림 되기 때문에 0x30~0x38크기의 malloc 결과의 청크 사이즈는 0x40이 될것이기 때문에 우리는 0x40으로 만들어 줄 것이고
다음에 우리가 malloc할 때 0x30으로 할당 받을 것이다라는 이야기이다.

이제 다음 청크를 만들어 줘야한다.

free시 청크 뒤에 있는 청크의 사이즈를 검사한다. 그렇기에 정상적인 사이즈를 넣어야한다.



(검사 조건)


검사 조건은 이렇다.

SIZE_SZ란



(SIZE_SZ)


INTERNAL_SIZE_T의 크기가 SIZE_SZ 이고,
INTERNAL_SIZE_T는 size_t이다.
64비트에서는 8바이트 이므로 16바이트(0x10)보다 커야한다는 것이다.
당연한 것이, 청크의 헤더가 0x10이기 때문에 이거보다는 커야한다.

그 다음 조건은

av->system_mem 보다 작아야한다는 것




(system_mem)


av란 mstate로 정의 되어있고, mstate는 malloc_state 구조체이다.

이 구조체에 있는 system_mem은 할당된 메모리라고 나온다.
이 아레나가 할당된 크기를 말한다. 즉, 당연히 이 크기보다는 작아야한다는 것이다.

이런 아주 지극히 정상적인 크기의 값만 넣어주면 어떤 값이든 상관없다.

이 자료에서는 0x1234라는 값을 넣어주었다.



(두번째 청크 크기 설정)


여기서 배열 인덱스 9번에 적어주었는데, 그 이유는
우리가 첫번째 잡은 청크가 0x40의 크기였고 unsinged long long 크기가 8바이트 이기 때문에 총 배열 8개를 잡아먹고 그 다음 위치가 9이기 때문이다.

이제 우리가 만든 스택영역에 가짜 청크 두개를 만들었다.

이제 이 주소를 free 시키기 위해 포인터 변수에 이 주소를 먼저 넣어야한다.



(주소 입력)


인덱스가 2인 이유는 처음 0, 1은 헤더이다. free시킬 때는 user data영역의 주소를 가지고 free시키기 때문이다.

그 후 free 해준다.



(free)


그렇게 되면 지금 fastbin의 0x40의 리스트에 우리가 만든 가짜 청크의 주소 a의 주소가 들어있다.

이제 다시 malloc(0x30)을 하면 fastbin리스트에서 0x40의 주소를 찾아가 현재 있는 a의 주소를 return하게 되는 것이다.



(malloc)


공격 결과를 확인해보면 다음과 같다.



(공격 성공)

'Vulnerability_Tech > About Heap' 카테고리의 다른 글

(how2heap) - poison null byte  (0) 2018.02.20
malloc의 사용가능 영역(HEAP)  (0) 2018.02.20
(how2heap) - unsafe_unlink  (0) 2018.01.13
(how2heap) - fastbin_dup_into_stack  (0) 2018.01.11
(how2heap) - fastbin_dup  (0) 2018.01.04
이번 분석 자료는 unsafe_unlink 자료이다.

그 전까지 하던 자료와는 다르게 이해하기 매우 어려웠다.
heap영역의 chunk 구조를 정확하게 이해하고 있지 않았기 때문에
이해하기 어려웠었다.

이번 자료를 이해한다면 heap 영역의 chunk구조를 파악할 수 있다.

unlink의 취약점을 보여주는 자료다. 예전에 unlink 동작을 구현한 함수가 있는 워게임 문제를 푼 경험이 있는데, 이번 자료를 분석하면서 unlink 취약점을 위해 unlink 검증과정을 우회하는 경우도 확인 할 수 있었다.

목적
unlink취약점을 이용해 원하는 메모리 영역에 원하는 값을 적을 수 있다는 것.!

조건
1. fastbin 크기가 아닌 chunk 할당필요
2. global 변수 처럼 직접적으로 그 chunk에 접근할 수 있는 변수의 주소를 알고 있어야한다.
3. free 함수를 호출할 수 있어야한다.

먼저 글로벌 변수 chunk0_ptr을 선언해준다.



(글로벌 변수)


이제 이 포인터를 우리가 원하는 메모리 주소로 옮기고, 이 포인터를 이용하여 값을 적는것이 목표이다.

malloc_size = 0x80



(malloc)



2개의 heap chunk를 할당한다. 크기는 0x80으로, 실제 chunk 사이즈는(헤더포함) 0x90이 된다.

그 후 가짜 chunk를 첫번째 chunk안에서 만들어준다.
이 부분에서 이해하기 힘들었다.
우리가 가짜 chunk를 만드는 이유를 이해하고 넘어가야한다.

우리는 unlink 취약점을 이용할 것이다.
unlink 매크로를 호출할 수 있어야한다. unlink 매크로는 free가 일어날 때, 연속적으로 있는 chunk인지 확인 한 후 연속적이라면 인접 chunk와 병합을 하기 위해 호출되는 매크로이다.
그렇다면 unlink를 호출하려면 chunk 병합을 일으켜야한다.
또, unlink 과정에서 취약점을 이용하기 위해, 우리는 fd와 bk를 조작할 수 있어야한다.

우선 가짜 chunk를 첫번째 청크주소+0x10 (chunk0_ptr[0]) 에 만든다. ( 이 자료에서 )

chunk0_ptr[0]부터 chunk라고 생각을 해본다면
구조는
chunk0_ptr[0] -> 이전 청크가 free됬다면, PREV_CHUNK_SIZE
                         -> 사용중이라면, 사용자 data
chunk0_ptr[1] -> chunk_size
chunk0_ptr[2] -> free됬다면 fd ,아니라면 user data
chunk0_ptr[3] -> free됬다면 bk, 아니라면 user data

우리는 free된 것처럼 보이는 가짜 청크를 만들고 있으므로

fd와 bk를 설정해준다.



(fd 설정)



(bk 설정)


* fastbin에 관리되지 않는 사이즈므로 fd와 bk가 존재한다.

그렇다면 fd와 bk를 왜 저 값으로 설정하는 걸까?
바로 unlink 검증을 우회하기 위해서이다.

검증 조건 중 하나는
P->fd->bk와 P->bk->fd의 값이 P와 같은지 확인한다.
다르다면 corrupted double-linked list 에러 메세지를 출력한다.

이 때 넘겨주는 P는 unlink로 넘어가는 주소 인자이다.
unlink로 넘어가기 전 P는 병합 설정이 된 주소이다. 즉, 우리는 unlink 매크로 인자로 가짜 chunk의 주소가 넘어가게 만들 것이므로 우리가 적는 fd와 bk 자리가 P->fd, P->bk이다.
우리는 글로벌 변수 주소를 이용할 것이므로 &chunk0_ptr을 사용한다.
fd는 chunk주소에서 0x10을 더한 값
bk는 chunk주소에서 0x18을 더한 값
그렇기에 fd에는 글로벌 변수 주소 -0x18 값을 넣게 되면
P->fd 이 값이 글로벌 변수 주소 - 0x18이 되게 되고, 
P->fd->bk는 (글로벌 변수 주소 - 0x18) + 0x18 즉 글로벌 변수가 된다.
그런데 글로벌 변수에는 우리가 malloc으로 할당 받은 chunk의 +0x10(헤더) 주소이고 이 주소는 P의 값이다. 그러므로 P = P->fd->bk를 만족시킬 수 있고
같은 방법으로 P = P->bk->fd를 만족시킬 수 있다.

이러한 방법으로 검증 과정 중 하나를 우회할 수 있다.



(fd, bk 설정)


fd와 bk를 설정한 모습이다.

또 검증을 우회해야하는 것이 하나 있다.
바로 size이다.
P의 size와 next_chunk에서의 prev_size가 같아야한다.
(chunksize(P) != prev_size (next_chunk(P))

우리가 만든 가짜 chunk에서 현재 chunk의 size를 가지고 있는 곳은 chunk0_ptr[1] 위치이다.
여기의 값과 next_chunk(현재 chunk주소+size)의 값이 같아야한다.

이 자료의 64비트에서는 아래와 같이 우회했다.




(우회방법)


사이즈를 8로 해두었다.
그렇게 되면 chunk주소+0x8의 위치는 chunk0_ptr[1]로 같은 주소이고
두 값은 같을 수 밖에 없으므로 우회가 가능하다.

모든 우회 준비는 끝났다.
이제 unlink를 작동시키기 위해 두번째 청크의 헤더를 바꾸어야한다.

두번째 청크의 헤더를 바꾸기 위해 포인터로 직접 주소에 접근한다.



(헤더 설정)


처음 chunk1_hdr[0] 이 위치는 prev_chunk 사이즈이고, 우리는 여기서 0x80으로 설정한다.
원래 우리가 할당한 사이즈는 0x80으로 실제 헤더포함한 청크사이즈는 0x90이다.
그런데 0x10보다 작은 크기로 헤더에 알려주었다.
그렇게 되면 병합할 때 이전 청크 주소를 찾기 위해 이 사이즈를 이용하여 계산한다.
현재 청크 주소 + 이전 청크 주소 => 이 값을 unlink 매크로의 주소 인자P로 전달하는 것이다.
그렇기에 우리는 첫번째 청크보다 0x10 큰 위치부터 가짜 청크를 만들었기에 사이즈를 0x80이라고 알려주는 것이다. 
두번째 청크 주소 + 0x80의 위치가 우리의 가짜 청크 위치 주소이기 때문이다.

그리고 PREV_INUSE 비트를 0으로 만들어줘야 이전 청크가 free됬다고 인식이 되기 때문에 비트 마스킹을 해준다.

이제 끝났다.!

free를 한다!



(free)


이 과정에서 두번재 청크를 free하는데 우리가 속인 정보를 보고 인접한 청크(가짜 청크)랑 병합을 하고 unlink 과정을 진행한다.

free전 chunk0_ptr에 있는 값이다.



(free 전)



free 후를 비교해보면




(free 후)



free 후 chunk0_ptr의 값이 바뀐 것을 확인 할 수 있다.

이유는 unlink 과정에서
FD->bk = BK;
BK->fd = FD;
의 과정이 존재하는데 여기서 FD->bk와 BK->fd는 우리의 글로벌 변수 주소인 chunk0_ptr이다.

그렇기에 chunk0_ptr에 우리가 아까 fd로 설정한 주소가 들어가 있는 것이다.

아까 fd가 글로벌 변수에서 +0x18 한 위치의 값으로 설정했으므로

글로벌변수[3] 으로 직접 이 값을 바꿀 수 있다.



(공격)



만약 우리가 victim_string의 주소에 어떠한 값을 적고 싶다고 가정을 해보자.

그렇게 되면 이제 글로벌변수[3]에 해당 victim_string의 주소를 넣어주면



(공격)



위의 상황처럼 된다.
이제 글로벌 변수를 통해 해당 주소 위치에 접근 할 수 있고,

값을 직접 쓸 수 있다.



(메모리 작성)




(공격 모습)

'Vulnerability_Tech > About Heap' 카테고리의 다른 글

malloc의 사용가능 영역(HEAP)  (0) 2018.02.20
(how2heap) - house_of_spirit  (0) 2018.01.17
(how2heap) - fastbin_dup_into_stack  (0) 2018.01.11
(how2heap) - fastbin_dup  (0) 2018.01.04
(how2heap) - first_fit  (0) 2017.12.14


오늘 분석한 자료는 이전 포스팅에서 다루었던 fastbin_dup을 이용하여
malloc으로 할당받는 주소를 우리가 원하는 주소로 할당받는 것이다.

이번 자료에서는 그 주소를 stack 영역의 주소로 할당받는 것이 목적이다.

fastbin_dup과 같이, free list가 a -> b -> a 이러한 상황을 만들어야한다.
같은 chunck를 free 2번 시킨 후 하나를 다시 malloc으로 받은 후 그 주소에 접근할 수 있으므로 다음 chunk를 가리키는 포인터를 조작하는 방법이다.

이 기술을 직접 사용하기 위해서는
free 3번(같은 chunk로 2번), malloc으로 double free한 chunk를 받아오거나 double free한 chunk에 접근 할 수 있는 상황과 그 조작한 chunk를 malloc 시킬 수 있는 상황이어야한다.
그렇다면 stack 주소뿐 아니라 다른 주소로도 malloc을 이용해 받을 수 있다.
(단, chunk의 구조체중 size 멤버가 적절해야한다.)

fastbin_dup_into_stack 자료를 분석해보자.

먼저 주소로 받을 위치는 stack_var의 주소이다.



(우리의 목적 주소)


여기서 +8을 해준 주소를 목적이라고 한 것은 heap의 chunk 구조를 보면 앞의 8바이트가 chunk size정보가 들어있는 위치이기 때문이다. 

먼저, fastbin_dup과 같이 3개의 malloc을 해준다.



(3개의 malloc)


fastbin을 이용할 것이기에 8바이트를 할당한다.
최소 16바이트이기 때문에 16바이트의 user data 영역이 생기고 8바이트의 size, 8바이트의 prev_size영역으로 총 32바이트, 총 chunk의 크기는 0x20일 것이다.

첫번째 free 해준다.



(첫번째 free)


그 후 바로 a를 free를 하면 free list 맨 위에 있는 a가 같으면 에러가 나므로

b를 free해준 뒤 a를 free해준다.



(free)


지금 까지의 상황은
free list의 top에는 a 주소가 들어있고,
a는 b를 가리키고 b는 a를 가리키는 a->b->a 의 상황이다.

여기서 malloc을 해주어 a의 주소를 d에 넣고, 다시 malloc을 해준다.




(malloc)


위의 코드까지 진행이 된다면
d의 주소에는 기존 a의 주소가 들어있고, 현재 free list의 top에는 이전 a의 주소(현재 d의 주소)가 들어있을 것이다.

free chunk list에 있는 chunk에 접근할 수 있다.
-> 이전 a의 주소(현재 d의 주소)가 free chunk list에 있고, d로 접근할 수 있기 때문에!

그렇다면 이제 우리가 할당받을 주소는 stack주소이기 때문에
stack 영역에 chunk라고 속일 수 있게 흉내를 내야한다.
우리가 malloc(8)을 사용하여 받을 것이기 때문에 총 청크 사이즈는 0x20이다.(위에 설명)

그렇기에 stack_var주소 위치에 0x20을 넣는다.




(chunk 총 크기)


그 후 chunk size영역의 크기는 8바이트 이전의 주소를 적어줘야한다.

그렇기에 d의 sizeof의 값이 8바이트이기에 sizeof(d)를 해주었다.



(fd 포인터 조작)


위의 과정까지 진행이 되면

메모리의 상황은 다음과 같다.



(메모리 상황)



위와 같이 fd 포인터 위치에 stack 주소가 들어가게 된다.

그 후 malloc을 해주게 되면 a의 주소가 할당 되고,
fd 포인터에 적혀있는 stack 주소로 넘어가 chunk size를 확인해보고 우리가 0x20으로 적어주었으므로 fastbin list에 들어가게 된다.

그러므로 free list의 top에는 이제 우리가 정한 stack 주소가 들어가게 되고

이번에 다시 malloc을 하게 되면 stack 주소가 반환되는 것이다.



(stack 주소 malloc)



(stack 주소 반환)


(* 위의 캡쳐 사진은 따로따로 찍은 것으로 gdb메모리 상황과 stack 주소 반환 결과가 같지 않다.)

'Vulnerability_Tech > About Heap' 카테고리의 다른 글

malloc의 사용가능 영역(HEAP)  (0) 2018.02.20
(how2heap) - house_of_spirit  (0) 2018.01.17
(how2heap) - unsafe_unlink  (0) 2018.01.13
(how2heap) - fastbin_dup  (0) 2018.01.04
(how2heap) - first_fit  (0) 2017.12.14
오늘 분석한 자료는 fastbin_dup 이다.

분석에 앞서 fastbin을 알아보았다.
glibc의 heap 초기 할당은 Top Chunck에서 이루어진다. 

Top Chunk에서 필요한 양을 잘라서 할당해준다. 그 후 사용이 끝난 후에 free를 해주는데
이 free된 chunk들을 그냥 내비두면 엄청난 낭비일 것이다.

그렇기에 free된 chunk를 재사용하기 위해 bin구조로 관리를 하여 재할당 해준다.
bin 구조는 사이즈에 따라 small bin과 large bin으로 나뉜다.
그리고 특수한 목적에 따라 unsorted bin, fastbin 으로 더 나뉜다.

small bin은 512바이트 미만의 chunk들을 관리하는 bin이다. 최소 바이트 16바이트 부터 8바이트 단위로 분류한다.
이 small bin안에 fastbin이 있다.
small bin안에서 72바이트 미만의 chunk들을 fastbin으로 관리한다.
fastbin은 다른 bin들과 달리 single-linked list로 관리된다. 다른 bin들은 doubled-linked list로 관리된다.

fastbin의 특징은 이름과 같이 빠르게 재할당할 수 있다는 것이다. 원리는 다른 bin의 chunk들은 인접한 chunk가 free되면 병합하는 작업이 있는데 fastbin은 이러한 작업이 없다. 그렇기에 다른 bin과 달리 fd와 bk를 가지지 않는 single-linked list의 구조이다. 이러한 구조는 Internal Fragment를 발생시키지만 속도는 빠르다는 장점이 있다.




(fast bin 구조)


how2heap의 fastbin_dup은 fastbin의 특징을 이용해 double free 공격을 보여준다.

먼저 3개의 chunk를 할당한다.


(3개의 malloc 할당)


그 후 a를 free해준다.



(a chunk free)


double-free 공격이란 free를 같은 chunk를 2번 하는 것이다. 같은 chunk를 2번 free한 다는 것은 말이 안되고 가능하게 되면 free list에 같은 chunk가 중첩된것 2개 들어가게 되는 것이고 그렇게 되면 malloc으로 같은 주소를 여러번 받을 수 있다는 것이다.

그렇기에 바로 free(a)를 하게 되면 free-list의 top에 a의 주소가 있으므로 에러가 날 것이다.

대신 b를 free해준다.



(b chunk free)


그렇게 되면
free-list에 b -> a 이렇게 single linked list에 들어가있는 상황이다.

그러면 우리는 a를 다시 free시킬 수 있게 된다.



(a chunk 두번째 free)

지금 이 상황의 메모리를 보자



(메모리 상황)


fastbin에는 a -> b -> a 이렇게 들어있다. 그렇기에 a의 주소(6000)에 b의 주소(6020)가 들어있고,
b의 주소인 6020의 다음 노드는 6000 으로 설정되어 있는 것이다.

여기서 다시 3개의 malloc을 해보면 결과는 아래와 같이 a, b, a의 주소가 나오고 결과적으로 a의 주소가 두번 malloc이 되는 상황이 나온다.




(결과)

'Vulnerability_Tech > About Heap' 카테고리의 다른 글

malloc의 사용가능 영역(HEAP)  (0) 2018.02.20
(how2heap) - house_of_spirit  (0) 2018.01.17
(how2heap) - unsafe_unlink  (0) 2018.01.13
(how2heap) - fastbin_dup_into_stack  (0) 2018.01.11
(how2heap) - first_fit  (0) 2017.12.14

how2heap의 첫번째 분석 자료는 first_fit 이다.
first fit이란 메모리 할당 전략 중 하나이다.

대표적으로 3가지가 있는데
first fit, best fit, worst fit 이 있다.

기본적으로 리눅스에서 heap 할당 관련해서는 first fit전략을 취하고 있다.

first fit이란 할당 가능한 메모리 영역을 발견하면 바로 할당하는 방식이다.
best fit은 internal fragment가 최대한 작게  딱 맞는 크기의 영역을 할당하는 방식이고
worst fit은 정반대로 최대한 널널한 영역을 할당하는 방식이다.

first fit 프로그램은 취약점을 보여주는 프로그램은 아니고
리눅스의 메모리 할당방식을 직접 보여주는 프로그램이다.

과정을 따라가며 분석해보겠다.



(메모리 할당)


 처음에 malloc을 통해 heap영역을 2개 할당한다.

a와 b에 할당을 해준다.



(메모리 할당 확인)



프로그램 실행시 확인해 볼 수 있다.
첫번째로 할당한 영역의 주소는 46010 이고 (뒷자리만 부를것이다.)

두번째로 할당한 영역의 주소는 46220 이었다.



(할당된 메모리 사용)


그리고 할당된 주소를 사용한다. a라는 주소에 문자열을 입력해준다.



(메모리 해제)



그리고, 메모리 할당을 free를 이용해 해제해준다.
그렇게 되면 현재 a에 할당된 주소인 46010 주소는 free된 상태이며 사용가능한 주소로
bin 구조에 들어가게 된다. 
리눅스에서는 haep영역의 메로리 할당과 해제를 bin을 통해 관리한다. 
나중에 heap영역 메모리 요청이 들어오면 이 bin 구조를 탐색한 후 메모리 할당을 해줄 것이다.

이 bin 구조는 사이즈에 따라 fast bin, small bin, unsorted bin 여러가지 종류로 구분되고
구조도 조금씩 다르다. 이에 대한 설명과 분석은 해당 취약점 분석때 자세히 하겠다.
이번 자료에서는 first fit에 초점을 맞춰보겠다.

다시 이어서 malloc을 해준다.



(malloc)


malloc을 통해 얻은 주소를 c에 넣어준다.

자 그렇게 되면 아까 해제(free) 되었던 주소가 c에 들어갈 것이다.
bin 구조에서 탐색하다가 사용가능한 영역을 발견하고 바로 아까 해제해주었던 영역을 할당해 주는 것이다.

확인을 위해 이번엔 방금 할당된 주소를 사용해본다.



(메모리 사용)



방금 할당 받은 주소에 C라는 문자열을 적는다.

그리고 a변수 처음에 할당되었던 주소가 담긴 변수와

c변수 방금 할당했던 주소가 담긴 변수를 주소값과 안에 담긴 문자열을 출력해본다.



(같은 주소)


결과는 처음 할당했던 주소가 46010이었는데 해제(free)해준 뒤 다시 malloc을 통해 받은 주소가 똑같은 46010이다. 그리고 그 안에 있는 문자열은 thist is C! 로 정확히 같은 주소라는 것을 보여준다.


'Vulnerability_Tech > About Heap' 카테고리의 다른 글

malloc의 사용가능 영역(HEAP)  (0) 2018.02.20
(how2heap) - house_of_spirit  (0) 2018.01.17
(how2heap) - unsafe_unlink  (0) 2018.01.13
(how2heap) - fastbin_dup_into_stack  (0) 2018.01.11
(how2heap) - fastbin_dup  (0) 2018.01.04

+ Recent posts