├── .gitignore ├── LICENSE ├── bin └── main.go └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | pkg 10 | src 11 | 12 | # Architecture specific extensions/prefixes 13 | *.[568vq] 14 | [568vq].out 15 | 16 | *.cgo1.go 17 | *.cgo2.c 18 | _cgo_defun.c 19 | _cgo_gotypes.go 20 | _cgo_export.* 21 | 22 | _testmain.go 23 | 24 | .vagrant 25 | Vagrantfile 26 | 27 | *.exe 28 | *.test 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Remco Verhoef 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /bin/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "sync" 7 | "flag" 8 | "fmt" 9 | "os" 10 | "os/signal" 11 | "syscall" 12 | "crypto/rand" 13 | "time" 14 | "net" 15 | "github.com/lxc/go-lxc" 16 | _ "code.google.com/p/gopacket" 17 | _ "code.google.com/p/gopacket/pcap" 18 | ) 19 | 20 | // An uninteresting service. 21 | type Troje struct { 22 | } 23 | 24 | var containers map[string]*lxc.Container = make(map[string]*lxc.Container) 25 | 26 | func randString(n int) string { 27 | const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 28 | var bytes = make([]byte, n) 29 | rand.Read(bytes) 30 | for i, b := range bytes { 31 | bytes[i] = alphanum[b % byte(len(alphanum))] 32 | } 33 | return string(bytes) 34 | } 35 | 36 | func Copy(dst io.Writer, src io.Reader) (written int64, err error) { 37 | buf := make([]byte, 32*1024) 38 | for { 39 | nr, er := src.Read(buf) 40 | 41 | log.Printf("%s", buf[:nr]) 42 | 43 | if nr > 0 { 44 | nw, ew := dst.Write(buf[0:nr]) 45 | 46 | if nw > 0 { 47 | written += int64(nw) 48 | } 49 | 50 | if ew != nil { 51 | err = ew 52 | break 53 | } 54 | 55 | if nr != nw { 56 | err = io.ErrShortWrite 57 | break 58 | } 59 | } 60 | 61 | if er == io.EOF { 62 | break 63 | } 64 | 65 | if er != nil { 66 | err = er 67 | break 68 | } 69 | } 70 | 71 | return written, err 72 | } 73 | 74 | func forward(localConn net.Conn) { 75 | defer localConn.Close() 76 | 77 | log.Printf("Received new connection from %s\n", localConn.RemoteAddr()) 78 | 79 | c2 := containers[localConn.RemoteAddr().Network()] 80 | if c2 == nil { 81 | log.Printf("Creating new container for ip %s\n", localConn.RemoteAddr()) 82 | c2 = GetContainer() 83 | containers[localConn.RemoteAddr().Network()] = c2 84 | } 85 | 86 | // Get interface name 87 | var interfaceName string 88 | 89 | for i := 0; i < len(c2.ConfigItem("lxc.network")); i++ { 90 | interfaceType := c2.RunningConfigItem(fmt.Sprintf("lxc.network.%d.type", i)) 91 | 92 | if interfaceType == nil { 93 | continue 94 | } 95 | 96 | if interfaceType[0] == "veth" { 97 | interfaceName = c2.RunningConfigItem(fmt.Sprintf("lxc.network.%d.veth.pair", i))[0] 98 | } else { 99 | interfaceName = c2.RunningConfigItem(fmt.Sprintf("lxc.network.%d.link", i))[0] 100 | } 101 | } 102 | 103 | _ = interfaceName 104 | 105 | // Get ipaddress 106 | ip, err := c2.IPAddress("eth0"); 107 | 108 | if err != nil { 109 | log.Printf(err.Error()) 110 | return 111 | } 112 | 113 | dest := fmt.Sprintf("%s:22", ip[0]) 114 | 115 | /* 116 | // Start packet capture for monitoring (outgoing) container traffic 117 | if handle, err := pcap.OpenLive(interfaceName, 1600, true, 0); err != nil { 118 | panic(err) 119 | } else if err := handle.SetBPFFilter("tcp and port 80"); err != nil { // optional 120 | panic(err) 121 | } else { 122 | go func(handle *pcap.Handle) { 123 | packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) 124 | for packet := range packetSource.Packets() { 125 | log.Printf("PACKET: %s", packet) 126 | } 127 | }(handle) 128 | } 129 | */ 130 | 131 | // Start forwarding service 132 | log.Println("Forwarding connections") 133 | 134 | var conn net.Conn 135 | 136 | for { 137 | conn, err = net.Dial("tcp", dest) 138 | defer conn.Close() 139 | 140 | if err != nil { 141 | log.Println("Waiting for ssh connection to settle.") 142 | 143 | time.Sleep(time.Millisecond * time.Duration(250)) 144 | continue 145 | } 146 | 147 | break 148 | } 149 | 150 | var wg sync.WaitGroup 151 | 152 | wg.Add(1) 153 | go func() { 154 | defer wg.Done() 155 | 156 | _, err := Copy(conn, localConn) 157 | if err != nil { 158 | log.Fatalf("io.Copy failed: %v", err) 159 | } 160 | 161 | }() 162 | 163 | wg.Add(1) 164 | go func() { 165 | defer wg.Done() 166 | 167 | _, err := Copy(localConn, conn) 168 | if err != nil { 169 | log.Fatalf("io.Copy failed: %v", err) 170 | } 171 | 172 | }() 173 | 174 | wg.Wait() 175 | 176 | log.Printf("Connection closed.\n") 177 | 178 | // send email with delta of: 179 | // /var/lib/lxc/gvgVHiMV/delta0/ 180 | // and tcpdump 181 | } 182 | 183 | 184 | func GetContainer() *lxc.Container { 185 | c, err := lxc.NewContainer("u1") 186 | if err != nil { 187 | log.Fatalf(err.Error()) 188 | } 189 | 190 | defer lxc.PutContainer(c) 191 | 192 | c2_name := randString(8) 193 | 194 | log.Printf("Cloning new container %s\n", c2_name) 195 | err = c.CloneUsing(c2_name, lxc.Aufs, lxc.CloneSnapshot) // #c.CloneUsing("u6_2") 196 | if err != nil { 197 | log.Fatalf(err.Error()) 198 | } 199 | 200 | c2, err := lxc.NewContainer(c2_name) 201 | if err != nil { 202 | log.Fatalf(err.Error()) 203 | } 204 | 205 | defer lxc.PutContainer(c2) 206 | 207 | // TODO: StartContainer prevents goroutine to return properly 208 | log.Printf("Starting new container %s\n", c2_name) 209 | if err := c2.Start(); err != nil { 210 | log.Fatalf(err.Error()) 211 | } 212 | 213 | log.Printf("Waiting for container to settle %s\n", c2_name) 214 | if !c2.Wait(lxc.RUNNING, 30) { 215 | log.Fatalf("Container still not running %s\n", c2_name) 216 | } 217 | 218 | var dest string 219 | 220 | for { 221 | ip, err := c2.IPAddress("eth0"); 222 | 223 | if err != nil { 224 | log.Printf("Waiting for ip to settle %s (%s)\n", c2_name, err.Error()) 225 | time.Sleep(time.Millisecond * time.Duration(1000)) 226 | continue 227 | } 228 | 229 | dest = ip[0] 230 | break 231 | } 232 | 233 | log.Printf("Container %s got ip %s\n", c2_name, dest) 234 | 235 | // increase container reference 236 | lxc.GetContainer(c2) 237 | return (c2) 238 | } 239 | 240 | var base = flag.String("b", "", "The name of the lxc base container") 241 | func main() { 242 | flag.Parse() 243 | 244 | if *base == "" { 245 | log.Fatal("No base container defined.") 246 | } 247 | 248 | log.Printf("Troje started.") 249 | 250 | // TODO: queue instances for warm start 251 | 252 | // TODO: forward multiple ports 253 | // var waitGroup *sync.WaitGroup 254 | var ch2 chan bool 255 | 256 | 257 | go func() { 258 | listener, err := net.Listen("tcp", ":8022") 259 | if err != nil { 260 | log.Fatalf("net.Listen failed: %v", err) 261 | } 262 | 263 | defer listener.Close() 264 | 265 | for { 266 | conn, err := listener.Accept() 267 | if err != nil { 268 | log.Fatalf("listen.Accept failed: %v", err) 269 | } 270 | 271 | select { 272 | case <-ch2: 273 | log.Println("stopping listening on", listener.Addr()) 274 | listener.Close() 275 | return 276 | default: 277 | } 278 | 279 | 280 | // serve 281 | go forward(conn) 282 | } 283 | }() 284 | 285 | // TODO: housekeeping, cleaunup containers when inactive for x period 286 | 287 | ch := make(chan os.Signal) 288 | signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) 289 | log.Println(<-ch) 290 | 291 | log.Printf("Troje stopping. Cleaning up.") 292 | 293 | for _, container := range containers { 294 | log.Printf("Destroying container %s", container.Name()) 295 | if err := container.Shutdown(30); err != nil { 296 | log.Fatalf(err.Error()) 297 | } 298 | 299 | log.Printf("Waiting for container to shutdown %s\n", container.Name()) 300 | if !container.Wait(lxc.STOPPED, 30) { 301 | log.Fatalf("Container still not running %s\n", container.Name()) 302 | } 303 | 304 | if err := container.Destroy(); err != nil { 305 | log.Fatalf(err.Error()) 306 | } 307 | 308 | lxc.PutContainer(container) 309 | } 310 | 311 | log.Printf("Troje stopped.") 312 | } 313 | 314 | 315 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # TROJE 2 | 3 | Troje is a honeypot that creates a real environment within a physical of virtual machine using lxc containers. These containers will be created on the first connection with the desired service. For example ssh service. At the first connection the 'attacker' will get its own attack vector, where Troje will pass all traffic between the service and their own attack vector. All traffic within the lxc container will be monitored, also the changes to the drives are being recorded. 4 | 5 | WARNING: this is a proof of concept and hasn't been tested accordingly. 6 | 7 | This version is a proof of concept. With the proof of concept I want to test the following: 8 | 9 | - an individual lxc container can be used per remote address 10 | - the lxc container is safe enough and can be constrained for attacks to operate safely 11 | - all actions can be monitored (traffic, filesystem, ssh) 12 | - the honeypot is realistic 13 | 14 | ## Quick start 15 | 16 | ### Install (ubuntu): 17 | 18 | ``` 19 | apt-get install golang lxc aufs-tools 20 | 21 | go get http://github.com/lxc/go-lxc 22 | go get http://code.google.com/p/gopacket 23 | ``` 24 | 25 | ### Create base container: 26 | 27 | ``` 28 | lxc-create -t download -n troje_base -- --dist ubuntu --release trusty --arch amd64 29 | ``` 30 | 31 | ### Start Troje: 32 | 33 | ``` 34 | GOPATH=`pwd` go run ./bin/main.go -b troje_base 35 | ``` 36 | 37 | Now Troje is up and running and you can connect using SSH to Troje. When you connect, the troje_base container will be cloned and all current and following connections from the remote address will be directed to the cloned container. 38 | 39 | ## Contributing 40 | 41 | Contributions are welcome. 42 | 43 | ## Example 44 | 45 | ``` 46 | root@packer-vmware-iso:/vagrant# go run ./main.go -b u1 47 | 2014/08/12 11:40:55 Troje started. 48 | 2014/08/12 11:40:57 Received new connection from 172.16.84.1:53483 49 | 2014/08/12 11:40:57 Creating new container for ip 172.16.84.1:53483 50 | 2014/08/12 11:40:57 Cloning new container 3IeAsSTV 51 | 2014/08/12 11:40:57 Starting new container 3IeAsSTV 52 | 2014/08/12 11:40:57 Waiting for container to settle 3IeAsSTV 53 | 2014/08/12 11:40:57 Waiting for ip to settle 3IeAsSTV (getting IP address on the interface of the container failed) 54 | 2014/08/12 11:40:58 Waiting for ip to settle 3IeAsSTV (getting IP address on the interface of the container failed) 55 | 2014/08/12 11:40:59 Waiting for ip to settle 3IeAsSTV (getting IP address on the interface of the container failed) 56 | 2014/08/12 11:41:00 Waiting for ip to settle 3IeAsSTV (getting IP address on the interface of the container failed) 57 | 2014/08/12 11:41:01 Container 3IeAsSTV got ip 10.0.3.84 58 | 2014/08/12 11:41:01 Forwarding connections 59 | 60 | <<< TRAFFIC BETWEEN CONTAINER AND REMOTE HOST >>> 61 | 62 | 2014/08/12 11:41:01 ^C2014/08/12 11:41:03 interrupt 63 | 2014/08/12 11:41:03 Troje stopping. Cleaning up. 64 | 2014/08/12 11:41:03 Destroying container 3IeAsSTV 65 | 2014/08/12 11:41:04 2014/08/12 11:41:04 Connection closed. 66 | 2014/08/12 11:41:04 Waiting for container to shutdown 3IeAsSTV 67 | 2014/08/12 11:41:04 Troje stopped. 68 | ``` 69 | 70 | ## Todo: 71 | 72 | - create hot spare containers 73 | - serialize data 74 | - reporting of traffic (pcap) 75 | - reporting of delta (/var/lib/lxc/gvgVHiMV/delta0/) 76 | - how to monitor the commands? using ssh and created username / password? 77 | - custom ssl certicate? for intercepting https traffic? 78 | - how to compare the differences? Use overlayfs? 79 | - when to clean up and create report? 80 | - listen on multiple ip adresses, with different containers 81 | - configure custom forwarding 82 | - use pipes / multithreading / locks 83 | - code improvements 84 | - start container prevent goroutine to return 85 | - should we use ephemeral storage? 86 | - Go-ify source 87 | 88 | ## Creators 89 | 90 | **Remco Verhoef** 91 | - 92 | 93 | ## Copyright and license 94 | 95 | Code and documentation copyright 2011-2014 Remco Verhoef. Code released under [the MIT license](LICENSE). 96 | 97 | --------------------------------------------------------------------------------