Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fallback to Direct Connection when HTTP Proxy Connection Fails #1295

Merged
merged 1 commit into from
Jun 16, 2023

Conversation

RafBishopFox
Copy link
Contributor

@RafBishopFox RafBishopFox commented Jun 12, 2023

Addresses #1282. Adds a new advanced HTTP C2 option called fallback that tells the implant to attempt a direct connection to the C2 server if connection via a proxy server fails.

Generating the implant:

profiles new -b http://my-awesome-c2:8080/?proxy=http://unrealiable-proxy&fallback=true -o linux -d proxy-profile
profiles generate -s /tmp/implant proxy-profile

The order of the connections is as described in the documentation when fallback is true: HTTPS with proxy, HTTP with proxy, HTTPS direct, HTTP direct:

...
2023/06/12 08:20:58 transports.go:96: Yield c2 uri = 'http://my-awesome-c2:8080?proxy=http://unreliable-proxy&fallback=true'
2023/06/12 08:20:58 transports.go:96: Yield c2 uri = 'http://my-awesome-c2:8080'
2023/06/12 08:20:58 session.go:84: Next CC = http://my-awesome-c2:8080?proxy=http://unreliable-proxy&fallback=true
2023/06/12 08:20:58 session.go:84: Next CC = http://my-awesome-c2:8080
2023/06/12 08:20:58 transports.go:96: Yield c2 uri = 'http://my-awesome-c2:8080?proxy=http://unreliable-proxy&fallback=true'
2023/06/12 08:20:58 session.go:172: Connecting -> http(s)://my-awesome-c2:8080
2023/06/12 08:20:58 gohttp.go:97: Force proxy "http://unreliable-proxy"
2023/06/12 08:20:58 gohttp.go:107: Proxy URL = 'http://unreliable-proxy'
...
2023/06/12 08:21:01 httpclient.go:361: [http] http response error: Post "https://my-awesome-c2:8080/index.html?d=19i3678481398&qq=71142985": proxyconnect tcp: dial tcp unreliable-proxy:80: connect: no route to host
...
2023/06/12 08:21:04 httpclient.go:361: [http] http response error: Post "http://my-awesome-c2:8080/login.html?bt=4743l5319&v=573604007z153": proxyconnect tcp: dial tcp unreliable-proxy:80: connect: no route to host
...
2023/06/12 08:21:04 sliver.go:134: Reconnect sleep: 1m0s
2023/06/12 08:22:04 session.go:172: Connecting -> http(s)://my-awesome-c2:8080
2023/06/12 08:22:04 session.go:84: Next CC = http://my-awesome-c2:8080?proxy=http://unreliable-proxy&fallback=true
2023/06/12 08:22:04 transports.go:96: Yield c2 uri = 'http://my-awesome-c2:8080'
...
2023/06/12 08:22:04 httpclient.go:361: [http] http response error: Post "https://my-awesome-c2:8080/oauth/authenticate/index.html?up=3e3913589&z=448a423422009": http: server gave HTTP response to HTTPS client
...
2023/06/12 08:22:04 httpclient.go:355: [http] POST -> http://my-awesome-c2:8080/auth/oauth2/oauth2/login.html?bp=33913589&e=33o7578006792 (106 bytes)
2023/06/12 08:22:04 httpclient.go:404: [http] New session id: 6f5579b8b50a2f5297c877689581873e

The PR adds a C2 entry for a direct connection to the C2 server if the fallback option is set to true and the wininet driver is not specified. By default, fallback is not set. The C2 with proxy server is kept in the rotation in case the connection can go through it on future attempts.

@RafBishopFox RafBishopFox requested a review from a team as a code owner June 12, 2023 12:52
@moloch--
Copy link
Member

This should already be the default behavior, is there a bug in the existing implementation or how does this differ?

@RafBishopFox
Copy link
Contributor Author

RafBishopFox commented Jun 12, 2023

This should already be the default behavior, is there a bug in the existing implementation or how does this differ?

@moloch-- I think there is a bug in the implementation. Here is the log from an implant before the PR:

...
2023/06/12 10:34:51 transports.go:92: Yield c2 uri = 'http://my-c2:8080?proxy=http://non-existent-proxy:8080'
2023/06/12 10:34:51 transports.go:92: Yield c2 uri = 'http://my-c2:8080?proxy=http://non-existent-proxy:8080'
2023/06/12 10:34:51 session.go:84: Next CC = http://my-c2:8080?proxy=http://non-existent-proxy:8080
2023/06/12 10:34:51 session.go:84: Next CC = http://my-c2:8080?proxy=http://non-existent-proxy:8080
2023/06/12 10:34:51 transports.go:92: Yield c2 uri = 'http://my-c2:8080?proxy=http://non-existent-proxy:8080'
2023/06/12 10:34:51 session.go:172: Connecting -> http(s)://my-c2:8080
2023/06/12 10:34:51 gohttp.go:97: Force proxy "http://non-existent-proxy:8080"
2023/06/12 10:34:51 gohttp.go:107: Proxy URL = 'http://non-existent-proxy:8080'
...
2023/06/12 10:34:51 httpclient.go:339: [http] POST -> https://my-c2:8080/namespaces/api/namespaces/db/database/db/samples.html?vj=530973h09&x=91035656 (106 bytes)
2023/06/12 10:34:54 httpclient.go:345: [http] http response error: Post "https://my-c2:8080/namespaces/api/namespaces/db/database/db/samples.html?vj=530973h09&x=91035656": proxyconnect tcp: dial tcp non-existent-proxy:8080: connect: no route to host
2023/06/12 10:34:54 gohttp.go:97: Force proxy "http://non-existent-proxy:8080"
2023/06/12 10:34:54 gohttp.go:107: Proxy URL = 'http://non-existent-proxy:8080'
...
2023/06/12 10:34:54 httpclient.go:339: [http] POST -> http://my-c2:8080/db/oauth2/api/oauth2/oauth/db/samples.html?bd=530w97309&p=390_746u22 (106 bytes)
2023/06/12 10:34:57 httpclient.go:345: [http] http response error: Post "http://my-c2:8080/db/oauth2/api/oauth2/oauth/db/samples.html?bd=530w97309&p=390_746u22": proxyconnect tcp: dial tcp non-existent-proxy:8080: connect: no route to host
2023/06/12 10:34:57 session.go:178: http(s) connection error Post "http://my-c2:8080/db/oauth2/api/oauth2/oauth/db/samples.html?bd=530w97309&p=390_746u22": proxyconnect tcp: dial tcp non-existent-proxy:8080: connect: no route to host
2023/06/12 10:34:57 sliver.go:152: [session] failed to establish connection: Post "http://my-c2:8080/db/oauth2/api/oauth2/oauth/db/samples.html?bd=530w97309&p=390_746u22": proxyconnect tcp: dial tcp non-existent-proxy:8080: connect: no route to host
2023/06/12 10:34:57 sliver.go:132: Reconnect sleep: 1m0s
2023/06/12 10:35:57 session.go:172: Connecting -> http(s)://my-c2:8080
2023/06/12 10:35:57 gohttp.go:97: Force proxy "http://non-existent-proxy:8080"
2023/06/12 10:35:57 gohttp.go:107: Proxy URL = 'http://non-existent-proxy:8080'
2023/06/12 10:35:57 session.go:84: Next CC = http://my-c2:8080?proxy=http://non-existent-proxy:8080
2023/06/12 10:35:57 transports.go:92: Yield c2 uri = 'http://my-c2:8080?proxy=http://non-existent-proxy:8080'
...
2023/06/12 10:35:57 httpclient.go:339: [http] POST -> https://my-c2:8080/db/api.html?f=453p2p002&mw=93700123 (106 bytes)
2023/06/12 10:36:00 httpclient.go:345: [http] http response error: Post "https://my-c2:8080/db/api.html?f=453p2p002&mw=93700123": proxyconnect tcp: dial tcp non-existent-proxy:8080: connect: no route to host
2023/06/12 10:36:00 gohttp.go:97: Force proxy "http://non-existent-proxy:8080"
2023/06/12 10:36:00 gohttp.go:107: Proxy URL = 'http://non-existent-proxy:8080'
...
2023/06/12 10:36:00 httpclient.go:339: [http] POST -> http://my-c2:8080/api/php/samples.html?hb=85236j195&x=1001r4_5167 (106 bytes)
2023/06/12 10:36:03 httpclient.go:345: [http] http response error: Post "http://my-c2:8080/api/php/samples.html?hb=85236j195&x=1001r4_5167": proxyconnect tcp: dial tcp non-existent-proxy:8080: connect: no route to host
2023/06/12 10:36:03 session.go:178: http(s) connection error Post "http://my-c2:8080/api/php/samples.html?hb=85236j195&x=1001r4_5167": proxyconnect tcp: dial tcp non-existent-proxy:8080: connect: no route to host
2023/06/12 10:36:03 sliver.go:152: [session] failed to establish connection: Post "http://my-c2:8080/api/php/samples.html?hb=85236j195&x=1001r4_5167": proxyconnect tcp: dial tcp non-existent-proxy:8080: connect: no route to host
2023/06/12 10:36:03 sliver.go:132: Reconnect sleep: 1m0s

The implant tries to connect through to the HTTPS C2 through the proxy then the HTTP C2 through the proxy. But when it wakes up from the reconnect sleep, it tries to connect through the proxy again. It never tries to connect directly from what I observed. I thought I could fix this by modifying the transport parameters on the fly. For example, we could change transport.Proxy in implant/sliver/transport/httpclient/gohttp.go:109 to a function that returned null after a connection through the proxy failed. I could not figure out a good way to track the state internally and after some thought, I decided that adding another version of the C2 would achieve the same thing and fit within the existing model. If I am wrong, I would love some advice on how to fix this in a better way :).

@moloch-- moloch-- merged commit 57e2111 into master Jun 16, 2023
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants