Skip to content

Commit

Permalink
Update RedGuard Version 23.05.14
Browse files Browse the repository at this point in the history
  • Loading branch information
wikiZ committed May 14, 2023
1 parent a49d862 commit 874a037
Show file tree
Hide file tree
Showing 12 changed files with 247 additions and 122 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## [23.05.14.2020] - 2023-05-14
### Added
- Sample Fingerprint Identify

## [22.08.03.1214] - 2022-08-03
### Added
- Support custom domain names for communication between intranet hosts
Expand Down
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,49 @@ The profile written by 风起 is recommended to use:

> <https://github.com/wikiZ/CobaltStrike-Malleable-Profile>
## Sample FingerPrint

RedGuard 23.05.13 has updated the trojan sample fingerprint recognition function, which is based on customizing the HTTP Header field of the Malleable Profile as the fingerprint “**sample salt value**” for uniquely identifying the same **C2 listener**/Header Host. In addition, the trojan sample fingerprint generated by combining other relevant request fields can be used to detect the custom sample liveliness. According to the attacker’s task requirements, the trojan sample fingerprint recognition function can perform “**offline operation**” on the samples you want to disable, to better evade malicious traffic analysis of the sample communication and the staged sample PAYLOAD attack payload acquisition analysis, and provide more personalized stealth measures for the attacker.

For different C2 listeners, we can give different aliases to the Malleable Profile configurations, customize the field names and values of related headers as the sample salt value, and use it as one of the distinctions between different samples. The following code is for illustration purposes, and in actual attack and defense scenarios we can use more realistic HTTP request packet fields as the basis for judgment.

```bash
http-get "listen2" {
set uri "/image.gif";
client {
header "Accept-Finger" "866e5289337ab033f89bc57c5274c7ca"; //Custom HTTP Header and Value
metadata {
print
}
}
}
```

**HTTP traffic**

![image.png](https://raw.githubusercontent.com/wikiZ/RedGuardImage/main/10b7b4d8f1d66bbf98e404332bf5d87.png)

As shown in the figure, we use the above sample Salt value and Host field as the basis for fingerprint generation. Here we know:

- **Salt Value:866e5289337ab033f89bc57c5274c7ca**
- **Host :redguard.com**

According to splicing the above values, the sample fingerprint is obtained as follows:

```bash
22e6db08c5ef1889d64103a290ac145c
```

Now that we know the above sample fingerprint, we can set the custom Header field and sample fingerprint in the RedGuard configuration file for malicious traffic interception. It is worth noting that we can extend multiple sample fingerprints, separated by commas, and the FieldName needs to be consistent with the Header field name configured in the Malleable Profile

![image.png](https://raw.githubusercontent.com/wikiZ/RedGuardImage/main/aa7488ece6370ff2559400a108664a4.png)

Because RedGuard’s configuration file is a hot configuration, we don’t need to restart RedGuard to intercept the samples we want to disable. When we want the sample to be reactivated, we just need to delete the relevant sample fingerprint from the RedGuard configuration file.

**Demonstration effect:**

![image.png](https://raw.githubusercontent.com/wikiZ/RedGuardImage/main/4d37798254ba9b5729ac886f90a10f7.png)

# 0x04 Case Analysis

## Cyberspace Search Mapping
Expand Down
213 changes: 107 additions & 106 deletions RedGuard.go
Original file line number Diff line number Diff line change
@@ -1,106 +1,107 @@
/**
* @Author 风起
* @contact: onlyzaliks@gmail.com
* @File: RedGuard.go
* @Time: 2022/5/4 10:44
**/

package main

import (
"fmt"
"os"
"strings"

"RedGuard/config"
"RedGuard/core"
"RedGuard/core/parameter"
"RedGuard/lib"
)

var logger = lib.Logger() // logger output model

type C2 struct {
Type string //Server interface{}
}

type c2Action interface {
serverInit()
}

type cobaltStrike struct {
action string
}

// ServerInit CobaltStrike module core method entry
func (cs *cobaltStrike) serverInit() {
cs.action = "CobaltStrike"
var (
proxy parameter.ProxyConf // Proxy configuration structure
cfg = lib.InitConfig() // config file object
num int // counting variable
)
// HTTPS Reverse proxy SSL certificate is created
lib.InitGenerateSelfSignedCert()
for key, value := range map[string]string{
"HTTPS": "/",
"HTTP": "/http",
} {
proxy.Action = key // Gets the reverse proxy listening port type
proxy.Pattern = value // Gets the pattern associated with the listening type
proxy.Port = lib.ReadConfig("proxy", fmt.Sprintf("Port_%s", key), cfg)
// When num is greater than 0, the main program is called out of the loop
if num > 0 {
break
}
num += 1
logger.Noticef("HostTarget: %s", lib.ReadConfig("proxy", "HostTarget", cfg))
// HTTP reverse proxy
go core.ProxyManger(proxy.Action, proxy.Port, proxy.Pattern)
}
// HTTPS reverse proxy
core.ProxyManger(proxy.Action, proxy.Port, proxy.Pattern)
// TODO CobaltStrike Core flow control method
}

func (c2 C2) configInit(args *parameter.Parses) {
c2.Type = args.C2Type
// Check C2 Server type
switch strings.ToLower(c2.Type) {
case "cobaltstrike":
// CobaltStrike Server initialize method
(&cobaltStrike{}).serverInit()
}
// TODO:Development Pending for other C2 frameworks
}

func main() {
fmt.Println(fmt.Sprintf(config.BANNER, config.VERSION, config.URL)) // output banner information.
// Create the tool argument
var (
parse parameter.Parses // Basic parameter structure
cert parameter.Cert // Certificate configuration parameter structure
_proxy parameter.Proxy // Proxy configuration parameter structure
)
core.CmdParse(&parse, &cert, &_proxy)
// Check whether RedGuard has been initialized
if num, isExits := lib.CreateConfig(parse.C2Type /* C2 Facility Type */, parse.ConfigPath); isExits {
switch {
case parse.Update:
lib.UpdateConfig(&cert, &_proxy) // Update RedGuard Config
logger.Notice("RedGuard Configuration file updated successfully!")
case parse.IP != "":
if lib.CheckIP(parse.IP) == false {
logger.Warning("Please enter a valid IP address")
os.Exit(0)
}
logger.Noticef("Search ipLookUpHelper: %s", parse.IP)
core.IPLookUp(parse.Location /* owning place to be verified */, parse.IP) // Query the location of an IP address
case num == 0:
// Select different C2 Server modes based on user parameters,default CobaltStrike.
(C2{}).configInit(&parse)
case num == 1: // Initialization is run for the first time
os.Exit(0)
}
}
}
/**
* @Author 风起
* @contact: onlyzaliks@gmail.com
* @File: RedGuard.go
* @Time: 2022/5/4 10:44
**/

package main

import (
"fmt"
"os"
"strings"

"RedGuard/config"
"RedGuard/core"
"RedGuard/core/parameter"
"RedGuard/lib"
)

var logger = lib.Logger() // logger output model

type C2 struct {
Type string //Server interface{}
}

type c2Action interface {
serverInit()
}

type cobaltStrike struct {
action string
}

// ServerInit CobaltStrike module core method entry
func (cs *cobaltStrike) serverInit() {
cs.action = "CobaltStrike"
var (
proxy parameter.ProxyConf // Proxy configuration structure
cfg = lib.InitConfig() // config file object
num int // counting variable
)
// HTTPS Reverse proxy SSL certificate is created
lib.InitGenerateSelfSignedCert()
for key, value := range map[string]string{
"HTTPS": "/",
"HTTP": "/http",
} {
proxy.Action = key // Gets the reverse proxy listening port type
proxy.Pattern = value // Gets the pattern associated with the listening type
proxy.Port = lib.ReadConfig("proxy", fmt.Sprintf("Port_%s", key), cfg)
// When num is greater than 0, the main program is called out of the loop
if num > 0 {
break
}
num += 1
logger.Noticef("HostTarget: %s", lib.ReadConfig("proxy", "HostTarget", cfg))
// HTTP reverse proxy
go core.ProxyManger(proxy.Action, proxy.Port, proxy.Pattern)
}
// HTTPS reverse proxy
core.ProxyManger(proxy.Action, proxy.Port, proxy.Pattern)
// TODO CobaltStrike Core flow control method
}

func (c2 C2) configInit(args *parameter.Parses) {
c2.Type = args.C2Type
// Check C2 Server type
switch strings.ToLower(c2.Type) {
case "cobaltstrike":
// CobaltStrike Server initialize method
(&cobaltStrike{}).serverInit()
}
// TODO:Development Pending for other C2 frameworks
}

func main() {
fmt.Println(fmt.Sprintf(config.BANNER, config.VERSION, config.URL)) // output banner information.
// Create the tool argument
var (
parse parameter.Parses // Basic parameter structure
_cert parameter.Cert // Certificate configuration parameter structure
_proxy parameter.Proxy // Proxy configuration parameter structure
_finger parameter.SampleFinger
)
core.CmdParse(&parse, &_cert, &_finger, &_proxy)
// Check whether RedGuard has been initialized
if num, isExits := lib.CreateConfig(parse.C2Type /* C2 Facility Type */, parse.ConfigPath); isExits {
switch {
case parse.Update:
lib.UpdateConfig(&_cert, &_proxy, &_finger) // Update RedGuard Config
logger.Notice("RedGuard Configuration file updated successfully!")
case parse.IP != "":
if lib.CheckIP(parse.IP) == false {
logger.Warning("Please enter a valid IP address")
os.Exit(0)
}
logger.Noticef("Search ipLookUpHelper: %s", parse.IP)
core.IPLookUp(parse.Location /* owning place to be verified */, parse.IP) // Query the location of an IP address
case num == 0:
// Select different C2 Server modes based on user parameters,default CobaltStrike.
(C2{}).configInit(&parse)
case num == 1: // Initialization is run for the first time
os.Exit(0)
}
}
}
12 changes: 6 additions & 6 deletions RedGuard.log
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
[2022-05-23 10:34:36] [RedGuard/core.ProxyManger] Proxy Listen Port :80 (HTTP)
[2022-05-23 10:34:36] [RedGuard/core.ProxyManger] Proxy Listen Port :443 (HTTPS)
[2022-05-23 10:34:57] [RedGuard/core.(*baseHandle).ServeHTTP] [REQUEST] GET /
[2022-05-23 10:34:57] [RedGuard/core.(*baseHandle).ServeHTTP] [REQUEST] 111.14.218.206 - Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.53
[2022-05-23 10:34:57] [RedGuard/core.(*baseHandle).ServeHTTP] [REDIRECT] Source IP: 111.14.218.206 -> Destination Site: https://360.net
[2022-05-23 10:34:57] [RedGuard/core.(*baseHandle).ServeHTTP] [REQUEST] 111.14.218.xxx - Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.53
[2022-05-23 10:34:57] [RedGuard/core.(*baseHandle).ServeHTTP] [REDIRECT] Source IP: 111.14.218.xxx -> Destination Site: https://360.net
[2022-05-23 10:35:52] [RedGuard/core.(*baseHandle).ServeHTTP] [REQUEST] GET /
[2022-05-23 10:35:52] [RedGuard/core.IPLookUp] {
"status": "0",
Expand All @@ -13,13 +13,13 @@
"data": [
{
"ExtendedLocation": "",
"OriginQuery": "111.14.218.206",
"OriginQuery": "111.14.218.xxx",
"appinfo": "",
"disp_type": 0,
"fetchkey": "111.14.218.206",
"fetchkey": "111.14.218.xxx",
"location": "山东省济南市 移动",
"origip": "111.14.218.206",
"origipquery": "111.14.218.206",
"origipquery": "111.14.218.xxx",
"resourceid": "6006",
"role_id": 0,
"shareImage": 1,
Expand Down Expand Up @@ -316,4 +316,4 @@
[2022-05-30 17:33:19] [RedGuard/core.(*baseHandle).ServeHTTP] [REQUEST] GET /js/config.js
[2022-05-30 17:33:19] [RedGuard/core.(*baseHandle).ServeHTTP] [REQUEST] 127.0.0.1 - Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/53L, like Gecko) Chrome/90.0.4430.212 Safari/537.36
[2022-05-30 17:33:19] [RedGuard/core.modifyResponse.func1.1] [RESPONSE] HTTP 301 Moved Permanently, length: 169
[2022-05-30 17:33:20] [RedGuard/core.modifyResponse.func1.1] [REDIRECT] Source IP: 127.0.0.1 -> Destination Site: https://360.net
[2022-05-30 17:33:20] [RedGuard/core.modifyResponse.func1.1] [REDIRECT] Source IP: 127.0.0.1 -> Destination Site: https://360.net
14 changes: 10 additions & 4 deletions config/RedGuard_CobaltStrike.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package config

var RedGuardConfig = `[cert]
# User Optional name
DNSName = *.aliyun.com,manager.channel.aliyun.com,*.acs-internal.aliyuncs.com,*.connect.aliyun.com,aliyun.com,whois.www.net.cn,tianchi-global.com
DNSName = *.aliyun.com,manager.channel.aliyun.com,*.acs-internal.aliyuncs.com",*.connect.aliyun.com,aliyun.com,whois.www.net.cn,tianchi-global.com
# Cert User CommonName
CommonName = *.aliyun.com
# Cert User Locality
Expand All @@ -23,7 +23,7 @@ Port_HTTPS = :443
# HTTP Reverse proxy port
Port_HTTP = :80
# RedGuard interception action: redirect / reset / proxy (Hijack HTTP Response)
drop_action = proxy
drop_action = redirect
# URL to redirect to
Redirect = https://360.net
# IP address owning restrictions example:AllowLocation = 山东,上海,杭州 or shanghai,beijing
Expand All @@ -35,7 +35,13 @@ AllowTime = *
# C2 Malleable File Path
MalleableFile = *
# Edge Host Communication Domain
EdgeHost = *
EdgeHost = *
# Edge Host Proxy Target example: EdgeTarget = 360.com
EdgeTarget = *
EdgeTarget = *
[SampleFinger]
# HTTP Request Header Field
FieldName = *
# Sample Finger example:xxxxxx,xxxxxx
FieldFinger = *
`
2 changes: 1 addition & 1 deletion config/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Github:%s
RedGuard is a C2 front flow control tool,Can avoid Blue Teams,AVs,EDRs check.
`
VERSION = "22.08.03 Alpha"
VERSION = "23.05.13 Alpha"
TITLE = "RedGuard"
LICENSE = "GPL-2.0"
URL = "https://github.com/wikiZ/RedGuard"
Expand Down
13 changes: 13 additions & 0 deletions core/ProxyFilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,22 @@ func ProxyFilterManger(req *http.Request) (status bool) {
allowIP = lib.ReadConfig("proxy", "AllowIP", cfg) // Obtain the online IP address whitelist
allowTime = lib.ReadConfig("proxy", "AllowTime", cfg) // Gets the allowed online time in the configuration file
malleableFile = lib.ReadConfig("proxy", "MalleableFile", cfg) // Obtain the profile path
fieldName = lib.ReadConfig("SampleFinger", "FieldName", cfg)
fieldFinger = lib.ReadConfig("SampleFinger", "FieldFinger", cfg)
banJA3 = data.BANJA3
banIP = data.BANIP
)

// sample finger verify
if f := req.Header.Get(fieldName); fieldName != "*" && fieldFinger != "*" && f != "" {
finger := lib.EncodeMD5(req.Header.Get("Host") + f)
logger.Noticef("Sample Finger: %s", finger)
if strings.Contains(fieldFinger, finger) /* finger Check*/ {
logger.Errorf("[DROP] Requested Sample Finger is forbidden to access")
return false
}
}

// Check whether ban ip is matched
for _, banAddr := range strings.Split(banIP, "\n") {
// Check whether the requested IP address is in the correct IP address format or network segment format
Expand Down
2 changes: 1 addition & 1 deletion core/ProxyHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@ func (h *baseHandle) ServeHTTP(write http.ResponseWriter, req *http.Request) {
if IPHash := lib.EncodeMD5(req.JA3); arrays.ContainsString(_addressArray, req.JA3) == -1 {
logger.Noticef("JA3 FingerPrint: %s", IPHash)
logger.Noticef("[REQUEST] %s %s", req.Method, req.RequestURI)
logger.Noticef("[REQUEST] %s - %s", req.RemoteAddr, req.UserAgent())
// Request filtering method
if !ProxyFilterManger(req) {
goto LOOK // Redirect to the specified site
}
logger.Noticef("[REQUEST] %s - %s", req.RemoteAddr, req.UserAgent())
_addressArray = append(_addressArray, IPHash) // Add to the list after verification for the first time
}
// Fetch directly from cache
Expand Down
Loading

0 comments on commit 874a037

Please sign in to comment.