[SCTF 2020] Eat the pie

T express에서 워낙 삽질을 많이 해서 대회 25분 남긴채 문제를 열어봤었다.

25분만에 풀뻔 했지만 아쉽게도 시간 내에는 풀지 못한 문제 ㅠㅠ 

대회 후에 밥먹고 풀어보니 바로 풀렸던 문제였다.

 

IDA 분석 & 취약점

void __noreturn pwnme()
{
  int buf[4]; // [esp+4h] [ebp-34h]
  int (*v1)(); // [esp+14h] [ebp-24h]
  int (*v2)(); // [esp+18h] [ebp-20h]
  int (*v3)(); // [esp+1Ch] [ebp-1Ch]
  void (__noreturn *v4)(); // [esp+20h] [ebp-18h]
  int v5; // [esp+24h] [ebp-14h]
  unsigned int v6; // [esp+2Ch] [ebp-Ch]

  v6 = __readgsdword(0x14u);
  memset(buf, 0, 0x28u);
  v1 = func1;
  v2 = func2;
  v3 = func3;
  v4 = func4;
  while ( 1 )
  {
    puts("\n0. Show flag");
    puts("1. Show environment variables");
    puts("2. Show directory contents");
    puts("3. Exit");
    printf("Select > ");
    fflush(stdout);
    read(0, buf, 0x10u);
    v5 = atoi((const char *)buf);
    if ( v5 <= 3 )
      ((void (*)(void))buf[v5 + 4])();
    else
      printf("Invalid input: %s\n", buf);
  }
}

 

우선, 문제 이름처럼 pie가 걸려 있었는데, 입력을 0x10만큼 보내주면 뒤에 있는 부분까지 다 출력되서 쉽게 pie_leak이 가능하다.

 

그리고 atoi 에서 oob가 나서 음수값 참조가 가능하다. 즉, 내가 원하는 곳으로 jump가 가능하다는 의미이다.

 

0x00000968 <+190>:	add    esp,0x10
   0x0000096b <+193>:	sub    esp,0x4
   0x0000096e <+196>:	push   0x10
   0x00000970 <+198>:	lea    eax,[ebp-0x34]
   0x00000973 <+201>:	push   eax
   0x00000974 <+202>:	push   0x0
   0x00000976 <+204>:	call   0x540 <read@plt>
   0x0000097b <+209>:	add    esp,0x10
   0x0000097e <+212>:	sub    esp,0xc
   0x00000981 <+215>:	lea    eax,[ebp-0x34]
   0x00000984 <+218>:	push   eax
   0x00000985 <+219>:	call   0x5f0 <atoi@plt>
   0x0000098a <+224>:	add    esp,0x10
   0x0000098d <+227>:	mov    DWORD PTR [ebp-0x14],eax
   0x00000990 <+230>:	mov    eax,DWORD PTR [ebp-0x14]
   0x00000993 <+233>:	cmp    eax,0x3
   0x00000996 <+236>:	jle    0x9b0 <pwnme+262>
   0x00000998 <+238>:	sub    esp,0x8
   0x0000099b <+241>:	lea    eax,[ebp-0x34]
   0x0000099e <+244>:	push   eax
   0x0000099f <+245>:	lea    eax,[ebx-0x13d5]
   0x000009a5 <+251>:	push   eax
   0x000009a6 <+252>:	call   0x550 <printf@plt>
   0x000009ab <+257>:	add    esp,0x10
   0x000009ae <+260>:	jmp    0x9bc <pwnme+274>
   0x000009b0 <+262>:	mov    eax,DWORD PTR [ebp-0x14]
   0x000009b3 <+265>:	add    eax,0x4
   0x000009b6 <+268>:	mov    eax,DWORD PTR [ebp+eax*4-0x34]
   0x000009ba <+272>:	call   eax

 

이 부분은 pwnme 함수에서 read 와 call eax 사이 부분인데,

call eax에서 다시 read 하는 부분으로 뛰게 되면, esp가 커지게 된다. 하지만 ebp는 그대로이기 때문에, 몇번 반복하다 보면

read함수 내부에서 read로 입력하는 부분이 esp보다 커지게 되고, 즉 read함수의 ret_addr을 덮을 수 있게 된다.

 

확인해보니, 2번 read로 뛰고, 3번째에 system + "/bin/sh" 를 넣게 되면 정확히 read의 ret_addr을 덮게 된다.

여기서 system은 system_plt가 아닌, func2의 함수 내부에 있는 call system을 사용했다.

 

 

# 알게된 점

저렇게 내부에서 반복하면서 read받는 위치가 esp보다 커지면서 read함수의 ret을 덮어본 것은 처음이었다. (새로 알게됨)

다른 풀이를 보니 push 0x10 다음으로 가서 read 입력 값을 0x10보다 더 많이 받음으로써 rop로 푸는 방법도 있었다.

 

'CTF > CTF_writeup' 카테고리의 다른 글

[SECCON CTF 2021] oOoOoO  (0) 2022.01.03
[SECCON CTF 2021] pppp  (0) 2021.12.28
[SCTF 2020] T express  (0) 2020.08.19
[Hack.lu 2018] Baby_Kernel_1  (1) 2020.04.04
[zer0pts] dirty laundry  (0) 2020.03.12
  Comments,     Trackbacks