Commit: 684cc056787857ec61d9abebeaabdba776b4f6d4 Author: Vi Grey Date: 2023-11-20 15:03 UTC Summary: Initial commit README.txt | 25 ++++++++++ go.mod | 3 ++ gopher-server.go | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ index.gph | 6 +++ index.gph-example | 20 ++++++++ polyglot-instructions.txt | 38 +++++++++++++++ 6 files changed, 291 insertions(+) diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..6e8dcdc --- /dev/null +++ b/README.txt @@ -0,0 +1,25 @@ +Gopher Server +-------------- + +A gopher server for an executable+ZIP polyglot file that hosts the +content in the ZIP file portion of the polyglot file. + +Instructions to produce the polyglot file are included in the file +`polyglot-instructions.txt`. + +An `index.gph` file is included. It is incredibly important that the +tabs and the first character of each line be correct, as gophermap +files are quite picky. An example index gophermap file is included at +`index.gph-example`, which is a gophermap for the tmp.0ut zine for +Volume 1. + +The gopher server is expecting an index.gph file at the root of the +ZIP file. + +Upon running the server, the Gopherhole will be listening at TCP +"localhost:2311" unless specified using the --host and/or --port flag +arguments. For more details, you can run the server with -h or --help. + +Port 2311 was chosen for the 2023-11 release of tmp.0ut Volume 3 and +because it's an unprivileged port, unlike port 70, which is used by +default for Gopherholes. diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f1dd109 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module gopher-server + +go 1.15 diff --git a/gopher-server.go b/gopher-server.go new file mode 100644 index 0000000..45a6ee4 --- /dev/null +++ b/gopher-server.go @@ -0,0 +1,199 @@ +/* + +------------------------+ + | Gopher Server | + | Programmed by Vi Grey | + +------------------------+ + + This is meant to be compiled and prepended to a ZIP file to make it an + Executable+ZIP polyglot file with the intention of the ZIP file + hosting the content for the Gopherhole. Please make sure an + "index.gph" gophermap file is included in the root of the ZIP file. + If run without any arguments, the ZIP file will be the executable + itself, and the Gopherhole will be listening on localhost:2311 (2311 + chosen because 2023-11 for the release of tmp.0ut Volume 3) + + Do whatever the heck you want to with this code, just don't blame me + if anything happens... + + GO GOLDEN GOPHERS! +*/ + +package main + +import ( + "archive/zip" + "fmt" + "io" + "log" + "net" + "os" + "path/filepath" + "strconv" + "strings" + "time" +) + +var ( + port = 2311 + host = "localhost" + zipFilePath string + zipReader *zip.ReadCloser +) + +// Print error log and close program with error code 1 if err not nil +func handleErr(err error) { + if err != nil { + log.Fatalf("%s\nExiting...\n", err) + } +} + +// Open ZIP file and get Reader +func getZIPFileReader() { + var err error + zipReader, err = zip.OpenReader(zipFilePath) + handleErr(err) +} + +// Get request from gopher client and serve page from ZIP file if path +// exists inside of ZIP file +func handleGopherConnection(conn net.Conn) { + var rBuf []byte + var request string + // Continue reading from connection until first \r\n is found + for { + rBuf = make([]byte, 2048) + n, err := conn.Read(rBuf) + if err != nil || n == 0 { + break + } + request += string(rBuf[:n]) + // Gopher path request ends at first instance of \r\n + nlIndex := strings.Index(request, "\r\n") + if nlIndex > -1 { + reqPath := request[:nlIndex] // Gopher path request + if reqPath == "" || reqPath == "/" { + // Set path to index.gph if request is root path + reqPath = "index.gph" + } + for _, zipFile := range zipReader.File { + if filepath.Join("/", reqPath) == filepath.Join("/", zipFile.Name) { + // /reqPath is in zip directory root + f, err := zipFile.Open() + handleErr(err) + defer f.Close() + // Write file content from zip file to connection + io.Copy(conn, f) + conn.Write([]byte("\r\n.\r\n")) + return + } + } + // Send error to gopher client. Essentially the same as HTTP 404 + conn.Write([]byte("3The selected resource does not exist\t\t" + + "fake\tfake\t0\r\n.\r\n")) + return + } + } + // Send error to gopher client. Essentially the same as HTTP 400 + conn.Write([]byte("3Invalid Request\t\tfake\tfake\t0\r\n.\r\n")) +} + +// Start the gopher server at specified host and port (default +// localhost:2311) +func startGopherServer() { + listener, err := net.Listen("tcp", + fmt.Sprintf("%s:%d", host, port)) + handleErr(err) + for { + if conn, err := listener.Accept(); err == nil { + go func() { + defer conn.Close() + // Close connection when request handled or after 30 seconds + conn.SetReadDeadline(time.Now().Add(30 * time.Second)) + handleGopherConnection(conn) + }() + } + } +} + +// Display help message and close program +func displayHelp() { + fmt.Printf(`Usage %s [OPTIONS]... + +Options: + -h, --help Print Help (this message) and exit + --host TCP address for gopher server to listen on + (default: "localhost") + --port TCP port to listen on (default: 2311) + --zip Path to ZIP file that hosts the content + (default: this executable file itself) +`, os.Args[0]) +} + +// Get a string value from args[x+1] +func getFlagStrValue(args []string, x, argsLen int) (string, error) { + if x+1 >= argsLen { + err := fmt.Errorf("%s: flag `%s` missing value\n Use `%s --help` for "+ + "details", os.Args[0], args[x], os.Args[0]) + return "", err + } + return args[x+1], nil +} + +// Get an integer value of 1-65535 from args[x+1] +func getFlagPortValue(args []string, x, argsLen int) (int, error) { + strVal, err := getFlagStrValue(args, x, argsLen) + if err != nil { + return 0, err + } + portVal, _ := strconv.Atoi(strVal) + if portVal < 1 || portVal > 65535 { + err = fmt.Errorf("%s: flag `%s` value must be a number value of "+ + "1-65535\nUse `%s --help` for details", + os.Args[0], args[x], os.Args[0]) + return 0, err + } + return portVal, nil +} + +func init() { + var err error + // Get executable path in case --zip flag not used + zipFilePath, err = os.Executable() + handleErr(err) + args := os.Args[1:] + argsLen := len(args) + // Flag checking (--host, -h, --help, --port, and --zip) + for x := 0; x < argsLen; x++ { + switch strings.ToLower(args[x]) { + case "-h", "--help": + displayHelp() + os.Exit(0) + case "--host": + // User specified TCP listening address + hostVal, err := getFlagStrValue(args, x, argsLen) + x++ + handleErr(err) + host = hostVal + case "--port": + // User specified TCP listening port + portVal, err := getFlagPortValue(args, x, argsLen) + x++ + handleErr(err) + port = portVal + case "--zip": + // User specified ZIP file path + zipFilePathVal, err := getFlagStrValue(args, x, argsLen) + x++ + handleErr(err) + zipFilePath = zipFilePathVal + } + } +} + +func main() { + getZIPFileReader() + defer zipReader.Close() + fmt.Printf("Starting Gopherhole at gopher://%s:%d\n", + host, port) + startGopherServer() +} diff --git a/index.gph b/index.gph new file mode 100644 index 0000000..44987f2 --- /dev/null +++ b/index.gph @@ -0,0 +1,6 @@ +itmp.0ut Volume 3 - November 2023 fake 0 +i fake 0 +03.0 Intro (tmp.0ut Staff) /txt/0.txt localhost 2311 +03.X Title (Author) /txt/path.txt localhost 2311 +03.X Title (Author) /txt/path.txt localhost 2311 +03.X Title (Author) /txt/path.txt localhost 2311 diff --git a/index.gph-example b/index.gph-example new file mode 100644 index 0000000..ff92070 --- /dev/null +++ b/index.gph-example @@ -0,0 +1,20 @@ +itmp.0ut Volume 1 - April 2021 fake 0 +i fake 0 +01.0 Intro (tmp.0ut Staff) /txt/0.txt localhost 2311 +01.1 Dead Bytes (xcellerator) /txt/1.txt localhost 2311 +01.2 Implementing the PT_NOTE Infection Method In x64 Assembly (sblip) /txt/2.txt localhost 2311 +01.3 PT_NOTE To PT_LOAD ELF Injector In Rust (d3npa) /txt/3.txt localhost 2311 +01.4 PT_NOTE Disinfector In Python (manizzle) /txt/4.txt localhost 2311 +01.5 Fuzzing Radare2 For 0days In About 30 Lines Of Code (Architect, s01den) /txt/5.txt localhost 2311 +01.6 The Polymorphic False-Disassembly Technique (s01den) /txt/6.txt localhost 2311 +01.7 Lin64.Eng3ls: Some Anti-RE Techniques In A Linux Virus (s01den, sblip) /txt/7.txt localhost 2311 +01.8 Linux.Midrashim.asm (TMZ) /txt/Linux.Midrashim.asm localhost 2311 +01.9 In-Memory LKM Loading (netspooky) /txt/9.txt localhost 2311 +01.10 Linux SHELF Loading (ulexec, Anonymous_) /txt/10.txt localhost 2311 +01.11 Return To Original Entry Point Despite PIE (s01den) /txt/11.txt localhost 2311 +01.12 Writing Viruses In MIPS Assembly For Fun (And No Profit) (s01den) /txt/12.txt localhost 2311 +01.13 Interview: herm1t (tmp.0ut Staff) /txt/13.txt localhost 2311 +01.14 GONE IN 360 SECONDS - Linux/Retaliation (qkumba) /txt/14.txt localhost 2311 +01.15 Linux.Nasty.asm (TMZ) /txt/Linux.Nasty.asm localhost 2311 +01.16 Linux.Precinct3.asm (netspooky) /txt/Linux.Precinct3.asm localhost 2311 +01.17 Underground Worlds (s01den) /txt/17.txt localhost 2311 diff --git a/polyglot-instructions.txt b/polyglot-instructions.txt new file mode 100644 index 0000000..c618125 --- /dev/null +++ b/polyglot-instructions.txt @@ -0,0 +1,38 @@ +Dependencies: + +* go >= 1.15 +* zip + + +1.) Compile go program: + +``` +go build +``` + +if you want to make the resulting binary file smaller, you can compile with: + +``` +go build -ldflags="-s -w" +``` + +2.) Concatenate the compiled program and ZIP file: + +``` +cat gopher-server zip-file.zip > cat.zip +``` + +3.) Fix ZIP file offsets: + +``` +zip -F cat.zip --out out.zip +``` + +4.) Make polyglot file executable if you want to run the file: + +``` +chmod +x out.zip +``` + +If you want to use --port or --host flags, please make sure index.gph +file at root of ZIP file has the matching port and host information.