본문 바로가기

Programming/Python Reverse Shell

남의 컴퓨터를 내 마음대로 다룬다? - Python Reverse Shell_002 - Binding the Socket and Listening for Connections (파이썬 리버스 쉘 - 소켓 묶기, 연결 대기)

앞 시간에는 멀리 떨어져 있는 다른 컴퓨터와 데이터를 주고 받는 통신을 하기 위해 소켓을 만들어 주었습니다. 이번에는 그렇게 만든 소켓의 데이터를 바인딩(묶어주기)하고 바인딩된 데이터를 가지고 상대 컴퓨터와 접속하게 하고, 그 접속을 서버(내 컴퓨터)에서 받아들이게 하는 과정까지 다루어 보도록 하겠습니다. 

Socket Binding(소켓 바인딩)

통신을 하기 위해서는 통신 기기간(여기서는 컴퓨터) 데이터가 어디에서 오고 어디로 가는지를 알아야 합니다. 이럴 때 사용하는 개념이 아이피(host)주소와 포트(port) 입니다. 그리고 이러한 host와 port를 바인드(묶어주기) 해주어야 어떤 데이터가 오가는지 쉽게 파악할 수 있습니다. 이는 송수신 데이터는 국제 규약인 프로토콜(Protocol)을 따라야 하기 때문입니다. 이에는 가령 신호 송수신의 순서, 데이터의 표현법, 오류 검출법 등이 해당됩니다.

소켓바인더 함수를 정의합니다.

서버는 자신의 컴퓨터가 될 것이고, 클라이언트는 타겟(원거리에 있는) 컴퓨터가 될 것입니다. 물론 아이피주소(IP Address)로 개략적인 위치는 가늠할 수 있지만, 해킹을 했다고 하더라도 타겟(클라이언트) 컴퓨터가 어디에 있는지는 알 수 없습니다. 그러므로 가급적 자신의 컴퓨터나 남에게 피해가 가지 않는 선에서 실습을 하시는게 좋습니다. 누군지 모르는 남의 컴퓨터에 무단으로 들락날락하면 유,무형의 제재를 받을 수 있으니 말이죠...;;


사용할 각각의 변수들을 글로벌로 지정합니다.

프린트 라인을 넣어 주어서 프리징(freezing)이 일어나고 있지 않다는 것을 확인하게 합니다. 그리고 포트로 지정한 99999는 임의의 숫자입니다. 따라서 출력하기 전에 스트링(String)으로 컨버팅을 해주기 위해 str()을 사용합니다.▼

데이터를 바인딩 하는것은 의외로 간단합니다. 소켓에 펑션(Function) 바인드(bind)를 호출하면 됩니다. 그 안에는 Tuple의 형태로 호스트(host), 포트(port)를 넣어줍니다.  호스트는 보통 IP address 가 됩니다.▼

이제 데이터들은 바인딩이 되었고 listen 할 준비가 되었습니다.

소켓에 펑션(function) listen()을 호출합니다. 인자로는 5가 적당합니다. 여기서 받은 인자 5는 back connection 을 뜻한다. 이 숫자를 넘어서는 connection 은 refusing 하겠다는 의미입니다. 너무 많은 커넥션을 받아들이면 자신의 컴퓨터(서버)에 큰 load가 걸릴 수 있기 때문입니다. 그리고 여기서 listen() 은 서버가 connection을 받아들이게 할 준비를 합니다.

만약 소켓 바인딩이 실패하면, 소켓 바인딩 에러를 출력하게합니다. 


 그리고 이 이후에 무엇을 할 것인지 보여주게 합니다. 여기서는 다시 바인딩을 시도하게 할 것이므로 "Retrying..." 메시지를 보여주게 합니다. 

그런 연후에 socket_bind() 를 호출하여 다시 시도하게 만듭니다.▼

Socket Accept(소켓 받아들이기)

이제 할 일은 누군가가 접속을 해 오면 그 접속을 받아들이게 하는 일입니다. 

만약 위에서 listen() 이 없다면, 소켓 accept() 는 작동하지 않습니다. 왜냐하면 Socket Accept이 발생하기 이전에는 반드시 listen() 이 구동되어져야 다음 단계인 accept 가 일어날 수 있기 때문입니다. 가령 문을 안 열었는데, 들어갈 수 없는 것처럼 말이죠. 집이나, 방으로 들어가려면 문을 열어야겠죠? 같은 이치입니다.

이렇게 listen()이 작동하고 있는 중에 socket_accept() 함수가 호출되면 새로운 커넥션(connection)을 수용하게 됩니다.▼

프로그램이 여기까지 아무런 문제없이 잘 진행되고 있는지 확인하기 위해, 우선 몇몇 정보를 프린트 아웃하게 해 보겠습니다.

만약 위의 accept()가 아무런 문제가 없다면, 이 커넥션은 성공한 것입니다. 그래서 다음과 같이 적을 수 있습니다.▼

언제든지 컴퓨터가 연결되면, address 의 첫 번째 인자로는 아이피주소가 할당됩니다. 그리고 두 번째 값은 포트넘버가 할당됩니다. 그래서 언제든지 클라이언트로부터 커넥션이 성립되면, 아이피 주소와, 포트넘버를 알 수 있습니다. address 는 누군가 접속하면, 그 데이터 정보를 알려주고, conn 은 커넥션 주고 받는 대화(conversation) 그 자체입니다.

언제든지 클라이언트로부터 접속이 되면, conn 은 어떤 명령(commands)를 주고 받을지 대기하고 있습니다. 그리고 명령(commands)를 입력 받으면 그대로 실행합니다. 이런 명령을 실행하고 반환되는 그 결과를 우리에게 다시 보내줍니다. 이렇게 보내온 결과를 보고 다시 타겟(클라이언트) 컴퓨터와 명령어를 주고 받으며 간접적으로 통신을 할 수 있게 되는 것입니다. 그래서 이러한 명령어를 포함하겔 될 conn 을 보내는 함수를 다음 시간에 정의해 보도록 하겠습니다.▼

마지막은 통신을 주고 받다가 종료를 하게 되면, conn 을 닫아주어 메모리를 효율적으로 사용할 수 있게 합니다.▼