본문 바로가기

시스템해킹/개념

puts 함수 분석을 통한 FSOP 공격 기법 분석

FSOP 공격에 대해서 공부하던 중, puts 함수가 실행될 때 공격이 터지는 것을 보고, puts 함수의 동작 방식을 분석하고, 어떤 부분에서 공격이 터지는 지를 확인해보았습니다.

 

먼저 처음에는 문자열의 길이를 계산하기 위해 j_strlen 함수를 호출합니다.

실제로 modify finished라는 문자열을 출력하기 위해서 puts가 호출되는데, 이 때문인지 rdi 레지스터에 "modify finished!"라는 문구가 들어가있는 것을 확인할 수 있었습니다.

그리고 반환 값을 보았을 때, 출력하고자 하는 문자열의 길이인 0x10이 반환 레지스터인 RAX에 들어가 있는 것을 확인할 수 있었습니다.

그 다음, 밑에 가다보면 이런 부분이 있습니다.

stdout + 17은 정확히 말하자면 stdout + 0x8 * 17과 동일하며, 이를 gdb로 확인했을 때는 다음과 같습니다.

 

즉, R8 레지스터에는 stdout 주소가 들어갑니다.

이는 뒤에 _InterlockedCompareExchange에 v5가 인자로 들어가면서 사용됩니다.

 

그 다음 계속 진행되다가 r14 레지스터에 rdi + 0xd8 값을 넣어줍니다. 이 위치는 _IO_FILE 구조체에서 vtable 필드이며, 현재는 FSOP 공격으로 fake_vtable 주소로 덮어써진 상태입니다.

 

그리고 후에 r14 + 0x38 부분을 호출합니다. 이 부분은 _IO_wfile_overflow 부분과 일치합니다.

 

그리고, 이 함수가 호출된 이후에 FSOP 공격이 터지며 쉘을 딸 수 있는 것을 확인했습니다. 따라서 이 _IO_wfile_overflow 함수를 따라가봐야 할 것 같습니다.

또한, _IO_wfile_overflow 내부에서 _IO_wdoallocbuf 함수를 호출하는데, 여기 내부에서 쉘이 따집니다.

 

__int64 __fastcall IO_wdoallocbuf(__int64 a1)
{
  __int64 result; // rax
  int v2; // edx
  __int64 v3; // rbp
  __int64 v4; // r12

  result = *(_QWORD *)(a1 + 160);
  if ( !*(_QWORD *)(result + 48) )
  {
    if ( (*(_BYTE *)a1 & 2) != 0 )
    {
      v2 = *(_DWORD *)(a1 + 116);
      v3 = result + 220;
      v4 = result + 216;
    }
    else
    {
      result = (*(__int64 (**)(void))(*(_QWORD *)(result + 224) + 104LL))();
      if ( (_DWORD)result != -1 )
        return result;
      result = *(_QWORD *)(a1 + 160);
      v2 = *(_DWORD *)(a1 + 116);
      v3 = result + 220;
      v4 = result + 216;
      if ( *(_QWORD *)(result + 48) )
      {
        if ( (v2 & 8) == 0 )
        {
          j_free();
          result = *(_QWORD *)(a1 + 160);
          v2 = *(_DWORD *)(a1 + 116);
        }
      }
    }
    *(__m128i *)(result + 48) = _mm_unpacklo_epi64((__m128i)(unsigned __int64)v4, (__m128i)(unsigned __int64)v3);
    *(_DWORD *)(a1 + 116) = v2 | 8;
  }
  return result;
}

 

이 코드에서 else 부분에  result = (*(__int64 (**)(void))(*(_QWORD *)(result + 224) + 104LL))();와 같이 함수를 호출하는 부분이 있습니다. 따라서 결론적으로 저 부분에 원 가젯이나 쉘코드가 들어가면 쉘을 딸 수 있습니다.