In this RingZer0 challenge, we are asked to visit http://challenges.ringzer0team.com:10013/ and are given 2 seconds to hash the provided message using the SHA512 algorithm. We must send the response as http://challenges.ringzer0team.com:10013/?r=response and to do that, we’ll be using some Golang.

Let’s declare the URI as a constant.

const uri = "http://challenges.ringzer0team.com:10013/"

We fetch the challenge page using the Get function from the http standard library, checking for errors along the way.

resp, err := http.Get(uri)
if err != nil {
	log.Fatalln(err)
}

We defer closing the response body when the program ends.

defer resp.Body.Close()

Next, we are going to use a library called goquery to parse the HTML in the body of the response.

doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
	log.Fatalln(err)
}

We will now match a single (goquery.Single) div element with the class “message” and read the text inside it.

message := doc.FindMatcher(goquery.Single(".message")).Text()

To grab the line which has the actual message, we split the lines and take the line at index 2 (which is line 3, remember computers begin indexing from 0).

line := strings.Split(message, "\n")[2]

Just to be on the safe side, let’s also trim out any leading or trailing tabs and whitespaces.

line = strings.Trim(line, " \t")

We can now find the SHA512 hash of the line using the standard crypto/sha512 library. For this we pass a byte slice representation of the string to the Sum512 function.

hash := sha512.Sum512([]byte(line))

To construct the new URI, we can use format strings. Here %s represents the original URI, ?r= is the parameter we are asked to supply and %x represents the hex digest of the hash.

flagUri := fmt.Sprintf("%s?r=%x", uri, hash)

Assuming that our program is quick enough to compute the hash withing 2 seconds 😅, we will fetch the flagUri. As usual, we defer closing the response body when the program ends.

flagPage, err := http.Get(flagUri)
if err != nil {
	log.Fatalln(err)
}
defer flagPage.Body.Close()

At this point, you could print the response body text which is what I did for the first time.

This might be a time to pause and ponder, perhaps try out the aforementioned technique.

For the sake of completeness, I will write the rest of the program so that it only prints the flag when run.


Let’s parse the response body using goquery again.

doc, err = goquery.NewDocumentFromReader(flagPage.Body)
if err != nil {
	log.Fatalln(err)
}

The flag is located in the div with the class “alert-info”.

flag := doc.FindMatcher(goquery.Single(".alert-info")).Text()

Finally, we print out the flag.

fmt.Println(flag)

Here’s the code in all it’s glory.

package main

import (
	"crypto/sha512"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"log"
	"net/http"
	"strings"
)

const uri = "http://challenges.ringzer0team.com:10013/"

func main() {
	resp, err := http.Get(uri)
	if err != nil {
		log.Fatalln(err)
	}

	defer resp.Body.Close()
	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		log.Fatalln(err)
	}

	message := doc.FindMatcher(goquery.Single(".message")).Text()
	line := strings.Split(message, "\n")[2]
	line = strings.Trim(line, " \t")
	hash := sha512.Sum512([]byte(line))

	flagUri := fmt.Sprintf("%s?r=%x", uri, hash)
	flagPage, err := http.Get(flagUri)
	if err != nil {
		log.Fatalln(err)
	}
	defer flagPage.Body.Close()

	doc, err = goquery.NewDocumentFromReader(flagPage.Body)
	if err != nil {
		log.Fatalln(err)
	}
	flag := doc.FindMatcher(goquery.Single(".alert-info")).Text()
	fmt.Println(flag)
}