오랜만의 힙 문제였지만, 크립토 문제를 풀다가 재미가 없어서 갈아탄 문제였다.
IDA 분석 & 취약점
void __cdecl use_ticket()
{
unsigned int v0; // eax
unsigned int index; // [rsp+4h] [rbp-Ch]
pass5 *p; // [rsp+8h] [rbp-8h]
printf("Index of ticket: ");
index = read_l();
if ( index <= 6 && passes[index] )
{
p = passes[index];
if ( p->ticket_type )
{
free(p);
}
else
{
puts("1) meal 2) safari 3) gift 4) ride");
printf("(1/2/3/4): ");
v0 = read_l();
if ( v0 == 2 )
{
if ( p->safari_pass )
--p->safari_pass;
}
else if ( v0 > 2 )
{
if ( v0 == 3 )
{
if ( p->giftshop_coupon )
--p->giftshop_coupon;
}
else if ( v0 == 4 )
{
++p->ride_count;
}
}
else if ( v0 == 1 && p->meal_ticket )
{
--p->meal_ticket;
}
if ( !p->meal_ticket && !p->safari_pass && !p->giftshop_coupon )
free(p);
}
puts("Thank you. Have a good time!");
}
else
{
puts("Wrong ticket!");
}
}
view_ticket 함수에서 보면 index가 signed_int라서 0~6까지 뿐만 아니라, 음수값을 입력하여 libc_leak이 가능했다.
그리고, free후에 딱히 다른 처리를 해주지 않아서 free한 청크에도 view를 할 수 있었고, 그래서 pie_leak또한 가능했다.
[+] PIE / LIBC leak
void __cdecl read_str(char *buf, unsigned int size)
{
ssize_t len; // [rsp+18h] [rbp-8h]
len = read(0, buf, size);
if ( len <= 0 )
{
puts("read error");
exit(0);
}
if ( buf[len - 1] == 10 )
buf[len - 1] = 0;
else
buf[len] = 0;
항상 힙 문제에서는 위처럼 read 함수를 만들어서 사용하는데 여기서 buf[len] = 0 을 사용함으로써 1바이트 오버플로우가 났다.
void __cdecl buy_ticket()
{
__int64 i; // [rsp+0h] [rbp-10h]
pass5 **p; // [rsp+8h] [rbp-8h]
LODWORD(i) = 0;
for ( p = passes; *p; ++p )
;
if ( (char *)p - (char *)passes <= 48 )
{
while ( 1 )
{
puts("Which do you want buy?");
puts("1. One ride ticket");
puts("2. One day ticket (3 meals, 1 safari pass, unlimited rides, 1 giftshop coupon)");
printf("(1/2): ", i);
HIDWORD(i) = read_l();
if ( HIDWORD(i) == 1 )
{
*p = (pass5 *)malloc(0x18uLL);
(*p)->ticket_type = 1LL;
goto LABEL_11;
}
if ( HIDWORD(i) == 2 )
break;
puts("No such ticket!");
}
*p = (pass5 *)malloc(0x30uLL);
(*p)->ticket_type = 0LL;
(*p)->meal_ticket = 3;
(*p)->safari_pass = 1;
(*p)->giftshop_coupon = 1;
(*p)->ride_count = 0LL;
LABEL_11:
printf("First name: ");
read_str((*p)->firstname, 8u);
printf("Last name: ", 8LL);
read_str((*p)->lastname, 8u);
}
else
{
puts("Sold out!");
}
}
위 함수에서 one ride ticket과 one day ticket을 구분하는 부분이 바로 fd / bk 영역의 다음 8바이트였는데
one ride ticket을 선택하여 malloc(0x18) 할당을 했지만 위에서 널 바이트 오버플로우를 이용하여 이를 one day ticket인 척 사용 가능하다
여기서, malloc(0x18) 후 one day ticket인 척 흉내낸다면, use 함수를 이용하여 다음 청크의 size / fd / bk 를 조작할 수 있다.
Exploit
malloc(0x30) => free
위 청크 사이즈 변경 후 한번 더 free. (Double Free !!)
double_free 를 트리거 했으니 fd 조작이 가능하기 때문에 fd를 malloc/free_hook 으로 덮어서 사용하려고 했다.
그런데 20.04에서는 one_gadget 조건이 까다로워서, free 할 청크 자리에 /bin/sh를 적어두고 free_hook을 system으로 덮어서 쉘을 땄다.
## 알게된 점
1. free 할 청크에 /bin/sh를 써두고 free_hook을 system으로 덮는 방법
2. tcache를 다룰 때, tcache에 들어가있는 개수가 저장되어 있음 => 개수가 0개로 되어있을 때는 tcache에서 꺼내오지 않기 때문에 유의할 것
'CTF > CTF_writeup' 카테고리의 다른 글
[SECCON CTF 2021] pppp (0) | 2021.12.28 |
---|---|
[SCTF 2020] Eat the pie (0) | 2020.08.19 |
[Hack.lu 2018] Baby_Kernel_1 (1) | 2020.04.04 |
[zer0pts] dirty laundry (0) | 2020.03.12 |
[zer0pts] nibelung (0) | 2020.03.12 |